【问题标题】:the effect on retain count of performSelector:withObject:afterDelay:inModesperformSelector:withObject:afterDelay:inModes 对保留计数的影响
【发布时间】:2010-12-11 01:33:13
【问题描述】:

一个简单的程序:

-(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?

【问题讨论】:

  • doMe 完成后的保留计数是多少?

标签: objective-c memory-management


【解决方案1】:

来自-[NSObject performSelector:withObject:afterDelay:inModes:] documentation

2013/11/01 回答

文档似乎已按预期更新,现在他们没有说目标对象被保留,但他们仍然在运行循环中提到了一个计划的计时器。

如果使用 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:... 和它的朋友们来这里是为了让我们的生活更轻松一些,因为它确保应用程序不会在计时器被触发时崩溃;)

希望对你有帮助

【讨论】:

  • 感谢您的文档指针。这回答了问题并且说得通。
  • 您好,您链接到的文档不同意您的声明。 NSObject 中有一堆“performSelector”变体,它们的文档没有说明保留接收器或对象。仅记录了 NSRunloop 方法 performSelector 以保留目标和对象。当心!!!
  • @MottiShneor 他明确提到了涉及 NSSTimers 的消息。剩下的一堆 performSelector 没有保留,因为它们不涉及计时器的
  • 顺便说一句,文档已更新,似乎不再保留该对象。
  • 感谢您的台词。您能否发布更新文档的链接?看不到它。但是,谁告诉我们 performSelector:withObject:afterDelay 变体使用 NSTimer?即使他们这样做了——在 performSelector:withObject:afterDelay 的文档中应该有关于对象和目标保留的说明。
【解决方案2】:

您似乎错误地认为doMe: 中的 NSLog 将在doSomething: 中的第二个之前执行。那是错误的。即使延迟为 0,performSelector:afterDelay:… 方法仍然使用 runloop 调度消息,因此它将在下一次 runloop 迭代时执行。

除此之外,几乎没有充分的理由去查看一个对象的保留计数——如果你确实看过,不要相信你所看到的。 retainCount 方法的结果充其量是令人困惑的,而在最坏的情况下则是彻头彻尾的欺骗性——举一个明显且极其常见的例子,保留计数不反映自动释放。

【讨论】:

  • 感谢有关不查看保留计数的建议。我想在受控示例中查看它也可能具有指导意义,但可能不是作为调试工具。
  • 我部分同意 Chuck 的观点,如果您将其与您自己计算的值进行比较,使用 retainCount 可能会令人困惑,因为您不知道该对象是否会被保留。但是,我认为这是检查对象生命周期的好方法。如果对象在时间 A 的 retainCount 是 X,那么我对对象做一些事情(它的保留计数可能会改变)然后我完成我正在做的事情,然后在那个时候(时间 B)它的 retainCount 应该再次回到 X。就像在这种情况下,在 performSelector... 之前和 doMe:retainCount 之后,我不知道具体是什么,但它们肯定是一样的。
  • @nacho4d:除了你完全错了,我不知道该说什么。单独的自动释放会导致“然后我完成我正在做的事情,然后在那个时候......它的 retainCount 应该再次回到 X”描述是不准确的。如果对象是自动释放而不是释放,它的保留计数仍会升高一段时间。它甚至在文档中说retainCount 方法不太可能产生有用的信息:developer.apple.com/library/mac/documentation/Cocoa/Reference/…
猜你喜欢
  • 2019-10-07
  • 1970-01-01
  • 2012-01-03
  • 2011-12-19
  • 1970-01-01
  • 2011-07-21
  • 1970-01-01
  • 2010-10-08
  • 1970-01-01
相关资源
最近更新 更多