10

在一些多点触控“在屏幕上绘图”代码的维护中,我遇到了一个关于如何在 touchesBegan:withEvent:、touchesMoved:withEvent: 和 touchesEnded:withEvent: 之间设置对触控实例的引用的错误。

该代码使用 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 对象的地址作为键是一个好主意:但这意味着使用 NSValue* 对象而不是 NSString* 对象(因此使用 CFDictionaryRef 而不是 NSDictionary 因为 UITouch 没有实现复制协议)。为什么存储地址的 NSString 表示而不是 NSValue 本身不能成为能够使用 NSDictionary 的简单解决方案? 更新:Apple 更新了页面并删除了通过 NSString 存储 UITouch 的示例,并且只提到了 not-object-at-all CFDictionaryRef 解决方案。

恐怕我远离 C 和指针的东西,需要帮助来理解这段代码中存在错误的原因。

4

4 回答 4

6

您可以对 ObjC 和 Swift 使用hash属性UITouch。跨多个事件(向上、向下、移动等)的相同触摸是相同的。它是一个,Int但您可以将其转换为String

于 2015-11-19T19:06:54.700 回答
4

当您使用 stringWithFormat 将 NSValue 实例转换为字符串时,我相信您实际上是在调用 [touchValue description],我不确定这是否能保证一致的结果。例如,如果描述包括一个指向 NSValue 实例本身的指针,就像许多对象一样?在这种情况下,存储完全相同的指针的两个不同的 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];
}

这将准确地告诉我们密钥发生了什么变化。但是,如果您只想解决您遇到的问题,我敢打赌,如果您直接使用 NSValue 对象作为键,它会起作用,因为它确实符合 NSCopying 协议。本质上,指针 (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 回答
0

这对我有用:

-(NSString * ) keyForTouch:(UITouch *) touch {
    return [NSString stringWithFormat:@"%lu", (unsigned long)[touch hash]];
}

//Example method, how to use:

-(UIView *) getViewForTouch:(UITouch*) touch{
    NSString * key = [self keyForTouch:touch];
    return [self.myDictionary objectForKey:key];
}
于 2020-06-08T21:36:11.970 回答
-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 回答