一个简单的程序:
-(void)doSomething {
NSLog(@"self rc=%d", [self retainCount]);
[self performSelector:@selector(doMe:) withObject:nil afterDelay:0 inModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
NSLog(@"self rc=%d", [self retainCount]);
}
-(void)doMe:(id)object {
NSLog(@"i'm done");
NSLog(@"self rc=%d", [self retainCount]);
}
输出:
self rc=1
self rc=2
i'm done
self rc=2
为什么保留计数增加到并保持在2?
答案 0 :(得分:13)
来自-[NSObject performSelector:withObject:afterDelay:inModes:] documentation:
2013/11/01回答
文档看起来像预期的那样更新,现在他们并没有说目标对象被保留,但他们仍然提到了runloop中的预定计时器。
如果使用NSTimer,则该对象必须由某人保留,否则将无法保证可以执行选择器,因为没有人可以确保该对象仍然存活。如果不使用ARC的weak
,它将崩溃。
在我看来,Apple编辑其文档时没有提到实现细节,尽管我认为这很明显。
上面的文档根本没有提到 retain 这个词,但这并不意味着不保留目标。
这是我在模拟器iOS7.0中的调试器中尝试过的:
(lldb) p (int)[self retainCount]
(int) $1 = 8
(lldb) expr (void)[self performSelector:@selector(description) withObject:nil afterDelay:1.0]
(lldb) p (int)[self retainCount]
(int) $2 = 9
(lldb) expr (void)[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(description) object:nil]
(lldb) p (int)[self retainCount]
(int) $3 = 8
结论:保留了对象,但这不是我们应该关心的。 先生解决了:p
上一个答案:(仍然有效)
此方法保留接收器和 之前的anArgument参数 执行选择器。
因为如果没有保留该对象,该对象可能会在执行之前被释放,并且您的应用程序将崩溃。
设置并尝试使用NSTimers触发方法时的逻辑是相同的。如果目标对象不再存在(已释放),则实际触发计时器时,您的应用程序将崩溃。所以performSelector:withObject:afterDelay:...
和它的朋友们在这里让我们的生活变得更轻松,因为它确保应用程序在计时器被触发时不会崩溃;)
希望有所帮助
答案 1 :(得分:4)
您似乎错误地认为doMe:
中的NSLog将在doSomething:
中的第二个之前执行。那是错的。即使延迟为0,performSelector:afterDelay:…
方法仍然使用runloop计划消息,因此它将在下一次runloop迭代时执行。
除此之外,几乎没有理由去查看对象的保留计数 - 如果你看的话,不要相信你所看到的。 retainCount
方法的结果充其量是混乱而最糟糕的是直接欺骗 - 对于一个显而易见且非常常见的例子,保留计数并不反映自动释放。