【问题标题】:Weak references in blocks and retain cycles块中的弱引用和保留周期
【发布时间】:2012-08-06 04:11:46
【问题描述】:

In this question,我询问了以下代码和保留周期:

__weak Cell *weakSelf = self;
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        UIImage *image = /* render some image */
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [weakSelf setImageViewImage:image];
        }];
    }];
    [self.renderQueue addOperation:op];

所有答案都表明这里没有必要使用弱引用,因为此代码不会导致保留周期。但是,在尝试更多代码时,以下确实会导致保留周期(如果我不使用弱引用,则不会释放当前视图控制器)

    //__weak ViewController *weakSelf = self;
    MBItem *close = [[MBItem alloc] initWithBlock:^{
        [self dismissModalWithDefaultAnimation:NO];
    }];
    NSMutableArray *items = [[NSMutableArray alloc] initWithObjects:close, nil];
    [self.childObject setItems:items];

为什么第二个会导致保留循环而不是第一个?

【问题讨论】:

  • 关键字是“Retain CYCLE”。比如,我留着你,你留着我,谁先放手?

标签: iphone objective-c ios


【解决方案1】:

如果您不使用__weak,您的旧代码会创建此保留循环

  • (NSBlockOperation *)op 保留外块
  • 外部块保留self(如果您不使用__weak
  • self 保留 (NSOperationQueue *)renderQueue
  • (NSOperationQueue *)renderQueue 保留 (NSBlockOperation *)op

除非其中一个链接断开,否则该循环中的任何对象都不能被释放。但是您向我们展示的代码确实打破了保留周期。当op 完成执行时,renderQueue 释放它,从而打破保留周期。

我怀疑您的新代码创建了这个保留周期:

  • (MBItem *)close 保留块
  • 块保留self
  • self 保留 childObject
  • childObject 保留 (NSMutableArray *)items
  • (NSMutableArray *)items 保留 (MBItem *)close

如果没有任何事情破坏这些链接之一,则循环中的任何对象都不能被释放。您没有向我们展示任何破坏保留周期的代码。如果没有显式中断它的事件(例如通过清除childObject.items),那么您需要使用__weak 来中断保留循环。

【讨论】:

    【解决方案2】:

    我无法告诉您第二个示例中保留循环的原因,因为我不知道 MBItem,但有两种不同的块使用模式。

    如果您希望您的代码块在任何情况下都能执行,那么您可以在代码块中使用self

    [startSomeOperationWithCompletionBlock:^{
        [self doSomeThing];
    }];
    

    该块保留对self 的引用,因此在执行该块之前不会释放self。但是在块执行之后,这个引用(和保留周期)就消失了。

    如果您可能希望 self 在块执行之前 被释放, 或者如果该块可能根本不会被调用, 那么你必须使用弱引用并检查块内的值:

    __weak MyClass *weakSelf = self;
    [startSomeOperationWithCompletionBlock:^{
        MyClass *strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf doSomeThing];
        }
    }];
    

    在这种情况下,该块不保留self,因此可以释放self。在这种情况下,weakSelf 会自动设置为 nil。因此,如果块最终被执行,你必须首先检查weakSelf是否仍然有效。 (或者你可以直接使用它,因为向nil 发送消息是无操作的。)

    在块内分配强引用strongSelf 可防止self 在块执行时被释放。

    【讨论】:

    • 如果块被执行了,但它仍然被保存在某个数组中,以便再次使用它怎么办?我猜 self 不会在这里被释放,直到数组被清除?
    • 是的,我认为这就是@robmayoff 在他的回答中所描述的。
    • 在块内创建对弱(或在我的情况下为 __unsafe_unretained)指针的强引用解决了我遇到的类似问题,即类在块执行过程中被释放。谢谢!
    • @MartinR 块只对其中使用的强引用进行强引用?我无法理解在块内传递weakSelf如何防止保留循环,而如果我在'self'中的强属性NSArray中添加strongSelf或weakSelf,则会导致保留循环。
    猜你喜欢
    • 2012-01-02
    • 2011-12-07
    • 2015-09-25
    • 2018-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-04
    相关资源
    最近更新 更多