Reputation: 8412
I want to implement behaviour similar to NSNotificationCenter
's -addObserverForName:object:queue:usingBlock:
. Using a method called something like
- (void)addRefetchObserver:(id)observer
handler:(FJRefetchHandler)handler;
a block should be stored for later invokation (FJRefetchHandler
is defined like this: typedef void(^FJRefetchHandler)(void)
.
Because I want to remove the block later, I also store observer
, and declare the following method:
- (void)removeRefetchObserver:(id)observer;
Usage would look like this:
// some place in code
[controller addRefetchObserver:self handler:^{
// refetch some stuff, i.e.
self.data = [self updateData];
}];
// some other place in code:
[controller removeRefetchObserver:self];
My question is: How should I implement -addRefetchObserver:handler:
so that I won't create any retain cycles? How should I store observers and handlers?
Apparently, NSNotificationCenter
somehow stores the observer without retaining it - otherwise I would not be able to call [center removeObserver:self]
in -dealloc
because -dealloc
would never get called.
Also, is there way to get around using __unsafe_unretained
when referencing self
in the block? i.e. like so:
__unsafe_unretained MyObject *blockSelf = self;
[controller addRefetchObserver:self handler:^{
blockSelf.data = [blockSelf updateData];
}];
Upvotes: 0
Views: 143
Reputation: 122489
Apparently, NSNotificationCenter somehow stores the observer without retaining it - otherwise I would not be able to call [center removeObserver:self] in -dealloc because -dealloc would never get called.
Yes, they keep a weak reference to it. You can easily keep weak references to observers in your classes too: if you need to have a collection of weak references, you can make a non-retaining version of NSArray or NSSet or NSDictionary, using the Core Foundation functions to create CFArray / CFSet / CFDictionary (they are toll-free bridged with, i.e. the same as, the NS equivalents), which allow you to explicitly specify the retaining/releasing behavior; so to not have a strong reference, you just have it do nothing on retain and release.
That you are storing the observer is kind of weird. With NSNotificationCenter, they store the observer because they need both the observer and selector to make the call. With yours, the block is already enough to make the call, and the block encapsulates all the logic of using the observer, so to store separately an "observer" seems strange. It seems like the only reason you have it is to have a way to remove it. For that matter, it could be any object, as long as you pass the same one in to add as you do to remove.
Whereas NSNotificationCenter has just one reference to the "observer", your system has two references -- one as the "observer" passed in, but you also have a reference to the block, which in all likelihood has a reference to the "observer" also. If you want it to work the same way as NSNotificationCenter, you need to make sure that these are both weak references. I think you've figured this out -- the direct "observer" reference you keep weak using what I described in the first paragraph; the block's reference to the "observer" must also be weak.
Also, is there way to get around using __unsafe_unretained when referencing self in the block? i.e. like so:
What you have is the correct way to weak-reference something from a block. More specifically, you should use __weak
if you are using ARC and targeting only iOS 5+. You should use __unsafe_unretained
(like you have) if you are using ARC otherwise. You should use __block
if you are not using ARC.
Upvotes: 1
Reputation: 21154
The better way to call self inside a block is to copy the reference of self into same weak variables and use it inside the block as;
MyController __weak *__weakController = controller;
[__weakController addRefetchObserver:self handler:^{
// refetch some stuff, i.e.
__weakController.data = [__weakController updateData];
}];
// some other place in code:
[controller removeRefetchObserver:self];
But, sometimes you may be dealing with long ongoing operation and meanwhile the controller may be released while the block is still in progress. Since block are placed on the stack and keep on executing, the better idea is to check if the controller still exists before calling some methods on it like;
MyController __weak *__weakController = controller;
[__weakController addRefetchObserver:self handler:^{
// some long on going tasks ...
MyController __strong *strongController = __weakController;
if(strongController)
strongController.data = [strongController updateData];
}];
// some other place in code:
[controller removeRefetchObserver:self];
Upvotes: 1