10

いくつかのマルチタッチ「画面上での描画」コードのメンテナンスで、touchesBegan:withEvent:、touchesMoved:withEvent:、および touchesEnded:withEvent: の間で touches インスタンスへの参照を設定する方法に関するバグに遭遇しました。

このコードは、次のように NSDictionary インスタンスを使用して touchesBegan:withEvent: にタッチの参照を格納します。

for (UITouch *touch in touches]) {
    NSValue *touchValue = [NSValue valueWithPointer:touch];
    NSString *key = [NSString stringWithFormat:@"%@", touchValue];
    [myTouches setValue:squiggle forKey:key];
}

touchesEnded:withEvent: でそれらを取得するには、次のようにします。

for (UITouch *touch in touches) {
    NSValue *touchValue = [NSValue valueWithPointer:touch];
    NSString *key = [NSString stringWithFormat:@"%@", touchValue];
    NSObject *valueFromDictionary = [myTouches valueForKey:key];
    [myTouches removeObjectForKey:key];
}

このコードの最後のビットでは、キーがディクショナリに認識されていないため、valueFromDictionary がたまたま nil になることがあります。時折、私は時間の約 1% を意味します。

これでコンテキストが設定されました。私の質問は次のとおりです。

  1. このコードにバグがある理由は何ですか? 特にエラーの頻度が非常に低いため、なぜ機能しないのかわかりません

  2. Apple の一部のドキュメントでは、UITouch オブジェクトのアドレスをキーとして使用することをお勧めします。ただし、これは、NSString* オブジェクトではなく NSValue* オブジェクトを使用することを意味します (したがって、UITouch はコピーを実装していないため、NSDictionary ではなくCFDictionaryRef を使用することを意味します)。プロトコル)。NSValue 自体ではなく、アドレスの NSString 表現を格納することが、NSDictionary を使用できるようにするための簡単な解決策ではないのはなぜですか? 更新: Apple はページを更新し、NSString を介して UITouch を格納する例を削除し、not-object-at-all CFDictionaryRef ソリューションについて言及しています。

残念ながら、私は C やポインターに詳しくないので、このコードにバグがある理由を理解する助けが必要です。

4

4 に答える 4

6

You can use hash of UITouch property for both ObjC and Swift. It is the same for the same touch across multiple events (Up, Down, Move, etc). It is an Int but you can convert it to String

于 2015-11-19T19:06:54.700 に答える
4

stringWithFormat を使用して NSValue インスタンスを文字列に変換する場合、基本的に [touchValue description] を呼び出していると思いますが、一貫した結果が保証されるかどうかはわかりません。たとえば、多くのオブジェクトがそうであるように、説明に NSValue インスタンス自体へのポインターが含まれていたらどうなるでしょうか。その場合、まったく同じポインターを格納する 2 つの異なる NSValue インスタンスは、異なる文字列キーを生成します。

次のコードを実行するとどうなるか見てみたいと思います。

for (UITouch *touch in touches) {
  NSValue *touchValue = [NSValue valueWithPointer:touch];
  NSString *key = [NSString stringWithFormat:@"%@", touchValue];
  NSObject *valueFromDictionary = [myTouches valueForKey:key];

  if (valueFromDictionary == nil) {
    NSLog(@"%@", [myTouches allKeys]);
    NSLog(@"%@", key);
  }

  [myTouches removeObjectForKey:key];
}

これにより、キーの変更点が正確にわかります。しかし、あなたが抱えている問題を解決したいだけなら、NSCopying プロトコルに準拠しているので、NSValue オブジェクトを直接キーとして使用すればうまくいくと思います。基本的に、ポインター (UITouch *) は、UITouch インスタンスがメモリ内のどこに格納されているかを一意に識別する単なる数値です。NSValue は、NSNumber とほぼ同じ方法でこの数値をカプセル化するため、配列への数値キーと考えることができます。タッチがメモリ内の同じ場所を占有し続ける限り (Apple が推奨するように)、タッチはキーとして完全に機能し、コードはより単純になります。

(touchesBegan:withEvent:)

for (UITouch *touch in touches]) {
  NSValue *key = [NSValue valueWithPointer:touch];
  [myTouches setValue:squiggle forKey:key];
}

(touchesEnded:withEvent:)

for (UITouch *touch in touches) {
  NSValue *key = [NSValue valueWithPointer:touch];
  NSObject *valueFromDictionary = [myTouches valueForKey:key];
  [myTouches removeObjectForKey:key];
}

キーと値のコーディングを使用するか、ディクショナリをプロパティ リストにシリアル化する必要がない限り、文字列キーに限定する理由はまったくありません。

于 2011-10-05T14:07:37.777 に答える
-1

使用しないでください

NSString *key = [NSString stringWithFormat:@"%@", touchValue];

キー用。最良の方法は、タッチポインタの値をキーとして設定することです

id key = touch;

または、文字列を使用する場合は、次の場合を使用します。

NSString *key = [NSString stringWithFormat:@"%x", touch]; // %x prints hex value of touch
于 2011-10-06T06:47:24.740 に答える