Reputation: 38025
I am attempting to implement a more or less straightforward callback mechanism using blocks in Objective-C. My concern, however, is how this will work with ARC and memory management.
Consider the scenario where I have a view controller that is displaying some relevant information from a model object that supports my block callback mechanism that is fired whenever a field on that object changes (assume I don't want to use KVO for this). The view controller registers a block that references self
and updates various aspects of the UI inside this block.
Now suppose the view disappears, but the model still exists in memory as it may be referenced elsewhere. What happens now when the object changes and calls its registered callback blocks? Presumably the blocks themselves will persist, but the moment I try to reference self
I will get nil. (?)
What I want to happen is to only actually call the callback block if its "target" (the view controller in this case) is still alive.
First question is, is there any way for the blocks themselves to be disposed-of automatically when the view controller is disposed? For example what would happen if I kept weak references to the blocks themselves? I'm guessing this wouldn't work because the blocks would disappear immediately with nothing else referencing them.
My second idea was to keep a weak reference to the target alongside the block itself, such that I can check the target reference to see if it is alive or not before calling the block (and if I discover that a target is dead I can simply remove the corresponding block).
My question then is what data structure should I use to store these--perhaps a struct or class that maintains a weak reference to a target and a strong reference to the block?
Upvotes: 1
Views: 773
Reputation: 122449
Now suppose the view disappears, but the model still exists in memory as it may be referenced elsewhere. What happens now when the object changes and calls its registered callback blocks? Presumably the blocks themselves will persist, but the moment I try to reference self I will get nil. (?)
This doesn't really make sense to me. You said that the view controller registered some callback blocks with reference self
(the view controller object itself). And you said the blocks themselves persist. Therefore, the view controller will persist, because it is being retained by the block which is alive.
Upvotes: 0
Reputation: 25619
Your second idea is actually kind of interesting.
typedef void (^NotificationHandler)(Foo* bar, What* ever);
@interface NotificationWrapper : NSObject
@property (nonatomic, weak) id canary;
@property (nonatomic, copy) NotificationHandler handler;
@end
You can keep these wrapper objects in a simple NSMutableArray, within perhaps a dictionary of notification handlers. Clients can register and unregister for notifications via, say:
- (void)registerForNotification:(NSString*)notificationKey
canary:(id)canary
handler:(NotificationHandler)handler;
When it's time to fire a notification, check if the canary is alive before calling the handler. If the canary is dead, remove the wrapper from your notification handler list.
for (NotificationWrapper* wrapper in notificationHandlers)
{
if (wrapper.canary)
{
wrapper.handler(foo, bar);
}
else
{
[deadHandlers addObject:wrapper];
}
}
for (id handler in deadHandlers)
{
[notificationHandlers removeObject:handler];
}
So what does this get you in the end? It saves the client from having to manually unregister its notifications, which is nice and avoids a common problem with KVO and NSNotificationCenter
. But it doesn't save the clients from dealing with potential reference cycles in the blocks they provide.
Upvotes: 2
Reputation: 1506
My second idea was to keep a weak reference to the target alongside the block itself, such that I can check the target reference to see if it is alive or not before calling the block (and if I discover that a target is dead I can simply remove the corresponding block).
This seems like it would work, but I believe recommended best practices are:
__weak typeof(self) weakSelf = self;
returnType (^myBlock)(parameterTypes) = ^returnType(parameters) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (strongSelf) {
// your block implementation
}
};
If you really don't want the block to fire at all then you can go ahead and keep a weak reference to the target with the block, but this seems like messy code.
Upvotes: 1
Reputation: 2464
I've been using following code snippet for some time and works perfectly:
__weak id this = self;
void (^blockObject)() = ^(){
__strong typeof(self) strongThis = this; // Or __strong MyClass *strongThis = this;
[strongThis someMethod];
};
If I remember correctly Apple suggested this approach at one of WWDC's. Reason for that is that if you use only __weak reference your object may release and your __weak reference will point to some garbage in memory. But when you make __strong reference to __weak, after your controller releases your __strong reference will be nil. Just as you want it to be.
Upvotes: 2