【问题标题】:iOS 4 blocks and retain countsiOS 4 阻止和保留计数
【发布时间】:2011-07-28 09:20:00
【问题描述】:

我刚刚开始使用街区和 Grand Central Dispatch。我被告知(并在Apple Documentation 中阅读)从块中引用的任何对象都会被保留。

例如:

^{  
    self.layer.transform = CATransform3DScale(CATransform3DMakeTranslation(0, 0, 0), 1, 1, 1);
    self.layer.opacity = 1;
}

"self" 被保留,所以它会泄漏。为避免这种情况,我需要将 self 分配给:

__block Object *blockSelf = self;

然后在我的块中使用blockSelf 而不是self

我的问题是:当您的块有更多代码并引用多个对象时会发生什么?我需要将它们全部分配给__block 对象吗?例如:

^{  
    [self doSomething];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"prevName == %@", artistName];
    [request setEntity:entity];
    [request setPredicate:predicate];

    Object *newObject = [[Object alloc] init];
    [someArray addObject];
    [newObject release];
}

【问题讨论】:

标签: iphone memory-management ios4 retain objective-c-blocks


【解决方案1】:

没有。当您的块保留一个保留它的对象时,就会出现问题。您的块将保留它引用的任何对象,除了那些用__block 注释的对象。因此:

// The following creates a retain cycle which will leak `self`:
self.block = ^{
  [self something];
};

self 保留 blockblock 隐式保留 self。这也将 如果您引用 self 的实例变量,就会发生这种情况。

// The following avoids this retain cycle:
__block typeof(self) bself = self;
self.block = ^{
  [bself something];
};

__block 注释的变量是可变的(对于指针,即 他们指向的地址可以更改);结果,没有意义 保留该对象,因为您需要将该对象视为局部变量 (例如,它可以被重新分配,影响块范围之外的对象)。因此,__block 不会被块保留。

但是,现在如果您尝试以某些方式使用此块,您可能会遇到无法预料的问题。例如,如果您决定以某种方式延迟此块的调用,并且在您执行该块时 self 已被释放,您的程序将崩溃,因为您正在向已释放的对象发送消息。那么你需要的是一个弱引用,它不是在非垃圾收集环境中开箱即用地提供的!

一种解决方案是使用MAZeroingWeakRef 包装您的块;这会将指针归零,因此如果您在释放 self 后尝试向 self 发送消息,您最终只会向 nil 发送消息:

MAZeroingWeakRef *ref = [MAZeroingWeakRef refWithTarget:self];
self.block = ^{
  [ref.target something];
};

我还实现了weak reference wrapper in Objective-C++,它提供了更轻量级语法的好处:

js::weak_ref<SomeClass> ref = self;
self.block = ^{
  [ref something];
};

因为js::weak_ref 是一个类模板,您将获得方便的强类型(也就是说,如果您尝试向引用发送它似乎没有响应的消息,您将在编译时收到警告到)。但是 Mike 的 MAZeroingWeakReference 比我的成熟很多,所以我建议使用他的,除非你想弄脏自己的手。

要详细了解__block 的问题和弱引用的用例,请阅读Avoiding retain cycles with blocks, a right wayJonathan Rentzsch's response

【讨论】:

  • 感谢乔纳森的详尽回答。因此,如果我理解正确,只要我不保留块以供重用,我什至不需要将 self 分配给 __block 变量?
  • 是的(尽管我通常只是为了安全起见)。只要您当时就在那里使用该块并且不复制它,您就不会有问题。
  • 您介意再解释一下吗。如果你看这个:developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/… 在 Objective-C 块部分,它有点不同,除非我读错了。我假设他正在制作一个由类方法使用的动画,结果,它不会自行保留块,从而使其安全。如果他使用任何其他实现,他会部分保留该块,它会泄漏。
  • @Pier-Olivier,我不太关注你。不知道动画是从哪里来的。但是,块的规则很简单: 1. 未标记__block 的引用变量是const-复制的(对于对象,这意味着制作指针的副本,而不是对象的副本); 2. 未标记__weak__block的引用对象被保留。
  • 记住:arc => __weak else => __block.
【解决方案2】:

我会说这取决于你用你的块做什么。如果您没有将它存储在任何地方并在定义位置使用它(例如使用块对数组进行排序),那么它会与其中引用的变量一起被释放(因为它是在堆栈中创建并标记为自动释放)。如果您将它存储在某处(数组、字典或可能将块传递给其他函数)copy 块并在传递之前将其与 autorelease 平衡。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-14
    • 2012-07-01
    • 2011-10-21
    • 1970-01-01
    • 2013-10-08
    • 2019-05-31
    • 1970-01-01
    相关资源
    最近更新 更多