2

从块文档:

在引用计数环境中,默认情况下,当您在块中引用 Objective-C 对象时,它会被保留。即使您只是引用对象的实例变量也是如此。

我正在尝试实现一个完成处理程序模式,其中在执行工作之前将块分配给对象,并且在执行工作后由接收器执行块。由于我是一个优秀的内存公民,块应该拥有它在完成处理程序中引用的对象,然后当块超出范围时它们将被释放。我知道我必须copy将块移动到堆中,因为块将在声明它的堆栈范围内存活。

但是,我的一个对象意外地被释放。经过一番尝试,当块被复制到堆中时,似乎某些对象没有保留,而其他对象则保留。我不确定我做错了什么。这是我可以生成的最小测试用例:

typedef void (^ActionBlock)(UIView*);

在某些方法的范围内:

NSObject *o = [[[NSObject alloc] init] autorelease];
mailViewController = [[[MFMailComposeViewController alloc] init] autorelease];
NSLog(@"o's retain count is %d",[o retainCount]);
NSLog(@"mailViewController's retain count is %d",[mailViewController retainCount]);
ActionBlock myBlock = ^(UIView *view) {
       [mailViewController setCcRecipients:[NSArray arrayWithObjects:@"test@recipient.com",nil]];
       [o class];
    };
NSLog(@"mailViewController's retain count after the block is %d",[mailViewController retainCount]);
NSLog(@"o's retain count after the block is %d",[o retainCount]);
Block_copy(myBlock);
NSLog(@"o's retain count after the copy is %d",[o retainCount]);
NSLog(@"mailViewController's retain count after the copy is %d",[mailViewController retainCount]);

我希望这两个对象在某个时候都被块保留,我当然希望它们的保留计数相同。相反,我得到了这个输出:

o's retain count is 1
mailViewController's retain count is 1
mailViewController's retain count after the block is 1
o's retain count after the block is 1
o's retain count after the copy is 2
mailViewController's retain count after the copy is 1

o( 的子类NSObject) 被正确保留并且不会超出范围。但是mailViewController不会保留,并且会在块运行之前被释放,从而导致崩溃。

4

3 回答 3

5

不要使用 -retainCount。

对象的绝对保留计数是没有意义的。

您应该调用release与导致对象被保留的次数完全相同的次数。不会少(除非你喜欢泄漏),当然也不会更多(除非你喜欢崩溃)。

有关完整的详细信息,请参阅内存管理指南

(摘自@bbum 的答案之一)


至于你的问题:

你真的在观察崩溃吗?或者你只是盲目地从臀部射击并认为它可能会崩溃?

从您发布的代码中,它似乎mailViewController是一个实例变量,在这种情况下,块将保留self而不是实例变量。而且由于您是您的实例变量,因此它会像您期望autoreleased的那样被清理干净。NSAutoreleasePool

总而言之:

  1. 不要使用-retainCount.
  2. 不要autorelease实例化您希望在此轮运行循环之外存在的变量。

编辑更多说明:

创建块时会发生以下情况:

  1. 检查其范围内的对象引用。有两个: self->mailViewControllero
  2. self->mailViewController是 struct ( self) 的成员,因此不直接保留。self改为保留。
  3. o是一个局部变量。保留它。

这是正确的行为。至于你的代码...

  1. o使用 +0 保留计数创建
  2. self->mailViewController使用 +0 保留计数创建
  3. myBlock使用 +0 保留计数创建。 o现在有一个 +1 RC,和self. self->mailViewController仍然有 +0 RC
  4. myBlock被复制 => +1 保留计数

快进到此运行循环周期的末尾。

  1. 当前的自动释放池已耗尽。所有保留计数为 +0 的对象都将被释放,包括self->mailViewController. self->mailViewController现在指向释放的内存,本质上是垃圾。

myBlock执行时快进到某个未来点

  1. myBlock尝试调用 上的方法self->mailViewController。但是,self->mailViewController不再指向有效对象,您的应用程序崩溃。

但是,如果mailViewController不是实例变量,那么我们需要查看更多代码。我认为您看到的行为不太可能是块运行时的问题,但有可能。

于 2011-01-03T20:50:03.663 回答
2

文档不再这么说。它现在正确地说:

在手动引用计数环境中,复制块时保留块内使用的局部变量。

于 2012-09-13T19:16:49.603 回答
-2

由于“mailViewController”是您当前类实例的成员,因此该块实际上在此处保留“self”。

于 2012-09-12T22:28:05.323 回答