Objective-C块回调,弱引用和被处置对象

时间:2014-12-02 23:03:27

标签: objective-c automatic-ref-counting objective-c-blocks weak-references

我正在尝试使用Objective-C中的块实现或多或少的直接回调机制。但是,我担心的是这将如何与ARC和内存管理协同工作。

考虑我有一个视图控制器从一个模型对象显示一些相关信息的场景,该模型对象支持我的块回调机制,只要该对象上的字段发生变化就会触发(假设我不想为此使用KVO) )。视图控制器注册一个引用self的块,并更新该块内的UI的各个方面。

现在假设视图消失,但模型仍然存在于内存中,因为它可能在其他地方引用。现在当对象发生变化并调用其注册的回调块时会发生什么?据推测,这些块本身会持续存在,但是当我试图引用self时,我将会得到零。 (?)

我想要发生的只是实际调用回调块,如果它的“目标”(在这种情况下是视图控制器)仍然存在。

第一个问题是,当处理视图控制器时,是否有任何方法可以自动处理块本身?例如,如果我对块本身保持弱引用会发生什么?我猜这不起作用,因为块会立即消失而没有别的引用它们。

我的第二个想法是在块本身旁边保持对目标的弱引用,这样我可以在调用块之前检查目标引用以查看它是否存活(如果我发现目标已经死了我可以简单地删除相应的块。)

我的问题是我应该使用什么数据结构来存储这些数据结构 - 可能是一个结构或类,它维护对目标的弱引用和对块的强引用?

4 个答案:

答案 0 :(得分:2)

我已经使用以下代码片段一段时间并完美运行:

__weak id this = self;
void (^blockObject)() = ^(){

    __strong typeof(self) strongThis = this; // Or __strong MyClass *strongThis = this;
    [strongThis someMethod];
};

如果我没记错,Apple会在WWDC的一个方面提出这种方法。原因是如果你只使用 __ weak 引用,你的对象可能会释放,你的__weak引用将指向内存中的一些垃圾。但是当您对 __ strong 进行 __ strong 引用时,在您的控制器发布 __ strong 引用后, nil 。正如你想要的那样。

答案 1 :(得分:2)

你的第二个想法实际上很有趣。

typedef void (^NotificationHandler)(Foo* bar, What* ever);

@interface NotificationWrapper : NSObject
@property (nonatomic, weak) id canary;
@property (nonatomic, copy) NotificationHandler handler;
@end

您可以将这些包装器对象保存在简单的NSMutableArray中,也可以是通知处理程序的字典中。客户可以通过以下方式注册和取消注册通知:

- (void)registerForNotification:(NSString*)notificationKey 
                         canary:(id)canary
                        handler:(NotificationHandler)handler;

当发出通知时,在调用处理程序之前检查金丝雀是否存活。如果金丝雀已死,请从通知处理程序列表中删除包装器。

for (NotificationWrapper* wrapper in notificationHandlers)
{
    if (wrapper.canary)
    {
         wrapper.handler(foo, bar);
    }
    else
    { 
         [deadHandlers addObject:wrapper];
    }
}

for (id handler in deadHandlers)
{
    [notificationHandlers removeObject:handler];
}

那到底是什么让你最终得到的?它使客户端不必手动取消注册其通知,这很好,避免了KVO和NSNotificationCenter的常见问题。但它并没有使客户免于处理他们提供的块中的潜在参考周期。

答案 2 :(得分:1)

  

我的第二个想法是在块本身旁边保持对目标的弱引用,这样我可以在调用块之前检查目标引用以查看它是否存活(如果我发现目标已经死了我可以简单地删除相应的块。)

这似乎可行,但我相信建议的最佳做法是:

__weak typeof(self) weakSelf = self;
returnType (^myBlock)(parameterTypes) = ^returnType(parameters) {
    __strong typeof(weakSelf) strongSelf = weakSelf;
    if (strongSelf) {
        // your block implementation
    }
};

如果你真的不想要阻止该块发射,那么你可以继续使用该块来保持对目标的弱引用,但这看起来像是凌乱的代码。

答案 3 :(得分:0)

  

现在假设视图消失,但模型仍然存在于内存中   因为它可能在别处引用。现在当对象发生了什么   更改并调用其注册的回调块?大概是   阻止自己会持续存在,但是我试图引用自我的那一刻   我会没事的。 (?)

这对我来说并不合理。您说视图控制器使用引用self(视图控制器对象本身)注册了一些回调块。你说块本身仍然存在。因此,视图控制器将保持不变,因为它正被活动的块保留。