在块内部是否总是需要使用弱引用来引用self?

42

我在使用块内的 self 时感到困惑,我查阅了一些苹果文档,但仍然找不到正确答案。

有些人总是说在块内使用 weak self,但也有人说只有在复制的块中使用 weak self,不一定总是需要使用。

示例1:

self.handler = ^(id response, NSError *error)
{
    self.newresponse = response; //use weak self here
};  

示例2:

使用weak self;

__weak myViewController *weakSelf = self;

[UIView animateWithDuration:interval delay:0.0 options:curve animations:^
{
    [weakSelf.view.superview setTransform:CGAffineTransformMakeTranslation(0, -106)];
    //in above is it use of weak is neassary 
}
completion:^(BOOL finished)
{

}];

没有脆弱的自我;

__weak myViewController *weakSelf = self;

[UIView animateWithDuration:interval delay:0.0 options:curve animations:^
{
    [myViewController.view.superview setTransform:CGAffineTransformMakeTranslation(0, -106)];

}
completion:^(BOOL finished)
{

}];

在上面的样本中,哪些是正确的...? **我正在使用ARC


也是 https://dev59.com/1GIj5IYBdhLWcg3wpmoH 的副本。 - Rose Perrone
2个回答

76

如果self将会持有块的引用,您应该仅使用弱引用(weak reference)来引用self

在您的示例中,您没有在self中保留块的引用,您只是在UIView animateWithDuration:内联使用了块,因此不需要使用__weak myViewController *weakSelf = self;

为什么呢?因为块会对从使用块的类中使用的任何变量保持强引用。这包括 self。现在,如果类实例本身保持对块的强引用,并且块保持对类实例的强引用,那么就会产生一个保留循环,这将导致内存泄漏。


3
在类方法(animateWithDuration)中,不需要使用weak self,因为self没有保留或拥有该块。我的理解是正确的吗? - ShivaPrasad
5
在示例1中,需要一个对self的弱引用。在示例2和3中,不需要对self进行弱引用。 - WDUK
2
谢谢,你的解释完全有道理。然而,我无法解释苹果在这个示例代码中使用weakSelf的原因:http://developer.apple.com/library/ios/#documentation/AddressBook/Reference/ABAddressBookRef_iPhoneOS/Reference/reference.html%23//apple_ref/doc/uid/TP40007099-CH991-SW4 - Ortwin Gentz
1
我犯了一个错误,没有意识到 self 没有保留 block;它只是框架的回调。抱歉。在我看来,在这种情况下,这是一种防御性编程方式。将其声明为 weak 是无害的,因为在概念上,在这里保留 self 没有意义(为什么回调会延长 self 的生命周期?)。这也意味着,如果有人想要稍后存储该块,则不需要更改其内容。最终,虽然在此处将其声明为 weak 不是必需的以防止泄漏,但这是一些程序员采取的风格,以确保鲁棒性。 - WDUK
如果我们只是在其中调用方法,那么是否需要使用弱引用来引用 self?比如 myblock {[self removeLoadingIcon];} - GoodSp33d
显示剩余6条评论

10

这里有一些演示@WDUK答案的代码:

typedef void (^SimpleBlock)();

@interface ObjectThatRetainsBlock : NSObject
@property(nonatomic, strong) SimpleBlock block;
@end

@implementation ObjectThatRetainsBlock

- (instancetype)init {
  self = [super init];
  if (self) {
    self.block = ^{ NSLog(@"Running block in %@", self); };
    self.block();
  }
  return self;
}

- (void)dealloc {
  NSLog(@"ObjectThatRetainsBlock is deallocated.");
}

@end

@interface ObjectThatDoesNotRetainBlock : NSObject
@end

@implementation ObjectThatDoesNotRetainBlock

- (instancetype)init {
  self = [super init];
  if (self) {
    SimpleBlock block = ^{ NSLog(@"Running block in %@", self); };
    block();
  }
  return self;
}

- (void)dealloc {
  NSLog(@"ObjectThatDoesNotRetainBlock is deallocated.");
}

@end

- (void)test {
  ObjectThatRetainsBlock *objectThatRetainsBlock =
      [[ObjectThatRetainsBlock alloc] init];
  ObjectThatDoesNotRetainBlock *objectThatDoesNotRetainBlock = 
      [[ObjectThatDoesNotRetainBlock alloc] init];
}

test方法打印:

Running block in <ObjectThatRetainsBlock: 0x7f95f3335e50>
Running block in <ObjectThatDoesNotRetainBlock: 0x7f95f3335c50>
ObjectThatDoesNotRetainBlock is deallocated.

请注意,在ObjectThatDoesNotRetainBlockinit方法中,我们将block创建为实例变量,但是当block超出范围时,我们没有保留对它的引用。

test方法中,当这两个对象超出范围时,观察到objectThatDoesNotRetainBlock被释放,因为它不是循环引用的一部分。

另一方面,objectThatRetainsBlock不会被释放,因为它是循环引用的一部分。它会在方法调用的作用域之外保留对该块的引用。

如果您需要更多解释,请参见此答案


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接