我正在尝试使用Objective-C中的块实现或多或少的直接回调机制。但是,我担心的是这将如何与ARC和内存管理协同工作。
考虑我有一个视图控制器从一个模型对象显示一些相关信息的场景,该模型对象支持我的块回调机制,只要该对象上的字段发生变化就会触发(假设我不想为此使用KVO) )。视图控制器注册一个引用self
的块,并更新该块内的UI的各个方面。
现在假设视图消失,但模型仍然存在于内存中,因为它可能在其他地方引用。现在当对象发生变化并调用其注册的回调块时会发生什么?据推测,这些块本身会持续存在,但是当我试图引用self
时,我将会得到零。 (?)
我想要发生的只是实际调用回调块,如果它的“目标”(在这种情况下是视图控制器)仍然存在。
第一个问题是,当处理视图控制器时,是否有任何方法可以自动处理块本身?例如,如果我对块本身保持弱引用会发生什么?我猜这不起作用,因为块会立即消失而没有别的引用它们。
我的第二个想法是在块本身旁边保持对目标的弱引用,这样我可以在调用块之前检查目标引用以查看它是否存活(如果我发现目标已经死了我可以简单地删除相应的块。)
我的问题是我应该使用什么数据结构来存储这些数据结构 - 可能是一个结构或类,它维护对目标的弱引用和对块的强引用?
答案 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
(视图控制器对象本身)注册了一些回调块。你说块本身仍然存在。因此,视图控制器将保持不变,因为它正被活动的块保留。