我的块没有保留它的一些对象

Dre*_*ord 2 iphone objective-c ios4 objective-c-blocks

来自Blocks文档:

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

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

但是,我的一个对象意外地被取消分配.在一些游戏之后,当块被复制到堆中时,似乎不保留某些对象,而其他对象是.我不确定我做错了什么.这是我可以生产的最小的测试用例:

typedef void (^ActionBlock)(UIView*);
Run Code Online (Sandbox Code Playgroud)

在某些方法的范围内:

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]);
Run Code Online (Sandbox Code Playgroud)

我希望块在某个时刻保留两个对象,我当然希望它们的保留计数是相同的.相反,我得到这个输出:

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
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*ong 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不是实例变量,那么我们需要查看更多代码.我认为你所看到的行为极不可能是块运行时的问题,但它是可能的.