【问题标题】:Is it necessary to use weak references to self always inside blocks..?是否有必要始终在块内使用对 self 的弱引用..?
【发布时间】:2012-10-23 01:48:54
【问题描述】:

我对在块中使用 self 感到困惑,我浏览了一些 Apple 的文档,但仍然找不到正确的答案。

有些人总是说在块中使用弱自我,但有些人说在复制的块中使用弱自我,而不是总是使用。

样本 1:

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

示例 2:

利用弱自我;

__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

【问题讨论】:

标签: cocoa-touch cocoa objective-c-blocks


【解决方案1】:

你应该只使用对self 的弱引用,如果self 将保持对块的引用。

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

为什么会这样?因为一个块将保留对它使用的任何变量的强引用,这些变量来自使用该块的类。这包括self。现在如果类实例本身保持对块的强引用,而块保持对类实例的强引用,你就有了一个retain循环,这会导致内存泄漏。

【讨论】:

  • 所以在类方法(animateWithDuration)中不需要弱自我,因为自我没有保留或拥有那个块,我对吗......?
  • 在示例 1 中 self.handler = ^(id response, NSError *error) { self.newresponse = response; //在这里使用弱自我 };我们需要一周自我,因为块是自我保留,自我是块保留,所以它是保留周期......?
  • @jeeva 在示例 1 中,需要对 self 的弱引用。在示例 2 和 3 中,不需要对 self 的弱引用。
  • 谢谢,您的解释完全有道理。但是,我无法在 Apple 的示例代码中解释 weakSelf 的使用:developer.apple.com/library/ios/#documentation/AddressBook/…
  • 好点,我错过了self 没有保留块的事实;它只是框架的回调。道歉。我相信在这种情况下,它是一种防御性编码。声明它是弱的并没有什么坏处,因为从概念上讲,在这里保留self 是没有意义的(为什么回调应该延长self 的寿命?)。这也意味着如果有人想稍后存储该块,他们不需要更改其内容。最终,虽然在这里声明它是弱的不是必需来防止泄漏,但它是一些程序员用来确保健壮性的一种风格。
【解决方案2】:

下面是一些演示@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 创建为ivar,但是当block 超出范围时,我们不会保留对它的引用。

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

另一方面,objectThatRetainsBlock 不会被释放,因为它是保留周期的一部分。它保留了超出方法调用范围的块。

如果您需要其他解释,请参阅this answer

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-15
    • 2013-11-30
    相关资源
    最近更新 更多