This question already has an answer here:
How can I create a reference cycle using dispatchQueues?
(1 answer)
Closed 2 years ago.
Help me resolve a tiny argument with a colleague. Weak self is not needed in this circumstance, right?
(He doesn't believe me)
__weak auto weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf _someHelper];
});
Correct, it is not always needed in cases like this. We often wouldn’t bother with this syntactic noise in this situation.
If it was dispatch_after or if this dispatch back to the main queue was buried inside some asynchronous call, then there’s an argument for weakSelf pattern (so you don’t keep strong reference longer than might be needed), but in your simple example, dispatching immediately to the main queue, the weak reference to self is not needed.
That having been said, it’s not wrong to adopt weakSelf pattern here. Just unnecessary.
The question in my mind is that if you’re dispatching back to the main queue, that suggests you are in the middle of something time consuming on some background queue. In that case, then refraining from keeping strong reference to self may well be prudent. We can’t say without seeing the broader context of where you are performing this code.
Note that ARC (automatic reference counter) can lead to situation where strong references will create a cycle. Such cycle will lead to memory leak (reference counter will never reach zero). In languages where garbage collection is available such cycle can be detected by gc and memory will be freed.
Now weak pointer prevent creation of permanent cycle of strong references.
Presented example is to general to tell if this is necessary or not.
There are some cases where some other object is responsible for object lifetime and invocation of _someHelper should be prevented when strong referees from other objects has expired (for example there action is not needed any more). In this case you need weak self.
In other case you need warranty that _someHelper is executed even if all other objects has lost contact with this object, so in such case you need that dispatch queue holds strong reference to object. In this case weak self is obsolete.
Related
I have a singleton class in my app that is created at app launch and always in use. I now am going to introduce an NSTimer that calls one of the singleton's methods periodically, so my understanding is that the timer will retain a strong reference to my singleton class (since the singleton class is the target). Is this correct? More importantly, is a strong retain cycle a problem for a singleton class that should live for the duration of the app? If so, why?
You ask:
I have a singleton class in my app that is created at app launch and always in use. I now am going to introduce an NSTimer that calls one of the singleton's methods periodically, so my understanding is that the timer will retain a strong reference to my singleton class (since the singleton class is the target). Is this correct?
Yes, NSTimer will maintain a strong reference to that instance of the singleton class. This strong reference is maintained until you invalidate the timer (or in the case of a non-repeating timer, until the timer fires).
More importantly, is a strong retain cycle a problem for a singleton class that should live for the duration of the app? If so, why?
The fact that a scheduled NSTimer instance maintains a strong reference to the target of its handler is not a problem, per se. You just have to be aware of this fact and manage your memory appropriately. All this means is that if you're done with some object that is a target of the NSTimer, that you are responsible for manually calling invalidate of that timer.
The only time that this strong reference to the timer's handler becomes problematic is when you're dealing with some object that that is of a more transitory nature (e.g. a view controller that might later be dismissed). Too often people think "oh, I'll just invalidate that NSTimer in the dealloc" method, but that doesn't work. The Objective-C dealloc method (as well as the Swift deinit method) is not called until there are no more strong references, so you can't try to resolve that timer's strong reference in dealloc, because dealloc can't get called until the timer is invalidated. Instead if you're done with the target of a NSTimer, you must manually invalidate the timer (e.g. in the case of the view controller example, people frequently do that in viewDidDisappear).
In your case, the fact that you have a scheduled repeating timer, calling the singleton method is not a problem. Just be aware that this singleton object (which will likely never fall out of scope anyway) cannot be deallocated as long as the scheduled NSTimer is still active. You'd have to invalidate the NSTimer in order to resolve the timer's strong reference to that target.
Two additional observations:
You've described this NSTimer behavior as a strong reference cycle (retain cycle). Technically, this isn't a strong reference cycle, but rather just a fact that a schedule NSTimer is retained by the run loop on which it's scheduled, and then the timer then retains the target upon which its handler is scheduled.
This is an somewhat academic distinction (because it manifests itself much like a strong reference cycle), but it's worth noting.
You should also be aware that instead of using a NSTimer, you can also use a GCD dispatch source timer. So, create a timer property in the class:
#property (nonatomic, strong) dispatch_source_t timer;
And then instantiate and start the timer:
typeof(self) __weak weakSelf = self;
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
dispatch_source_set_event_handler(self.timer, ^{
[weakSelf handleTimer];
});
dispatch_resume(self.timer);
This pattern, unlike the NSTimer approach, does not retain the target of the timer (or, in this case, the object upon which we're referencing via self) because of the use of weakSelf pattern in the block (which resolves any strong reference cycle). Thus we end up in a timer that is safely released when the object that contains it is deallocated, with no strong reference cycle.
I would consider it a problem. A singleton class is already hard to refactor and reuse. Adding a retain cycle to one is going to make the class that much harder to change or reuse in the future. When you discover that you need this class (or at least the set of responsibilities this timer is a part of) to not be a singleton you'll also need to remember or identify this memory leak and fix it as part of your refactor. Future you will probably be sad.
Instead you can write a class which follows good patterns (for memory management and otherwise) regardless of if it is accessed as a singleton or not. In your case that might mean keeping a weak reference to the timer ("Note in particular that run loops maintain strong references to their timers, so you don’t have to maintain your own strong reference to a timer after you have added it to a run loop.") and invalidating that timer if this object is deallocated.
The retain cycle might not be problematic at this time as you're using a singleton that doesn't need deallocation, but what if in the future someone else refactors the singleton into a regular class?
Another consideration is that NSTimer's are not very energy efficient, and Apple warmly recommends switching to GCD sources for performing timer-related tasks. You might even be able to break the retain cycle via weak references with this approach.
Imagine the following scenario using Manual Memory Management (aka non-ARC):
I have a VC that passes a block to a class method. Before the block being executed, the VC is popped up out of a UINavigationController. A weak reference in the form of __block MyVC *weakSelf = self is passed to the block which is then converted to MyVC *strongSelf = weakSelf (aka weak/strong dance). The block is never retained by any of the intervenients.
In this scenario, what I am seeing in my code is:
The dealloc of the VC is called when it is popped out.
The block is eventually is called.
The app crashes because I am accessing garbage (the strongSelf is pointing to it).
My question is: I don't want my VC to stay alive until the block eventually executes, but once the block is executed I want to confirm that strongSelf is valid.
I have checked this (non-trivial example by Apple) which doesn't work because it's designed with ARC in mind. So how can I have the same behaviour with MMM? Ideally I want to have what __weak does: if the retainCount reaches zero, I want its references to point to nil and not to garbage.
After reading this from Apple:
In some cases you can use __unsafe_unretained if the class isn’t
__weak compatible. This can, however, become impractical for nontrivial cycles because it can be hard or impossible to validate
that the __unsafe_unretained pointer is still valid and still points
to the same object in question.
Since I don't have access to __weak what I want to do is even possible?
I battled with this exact issue back in the iOS 4.x days. It's not easy without weak pointers giving you a hand!
If you are guaranteed that block is executed on the main thread at a later point (i.e. where strongSelf is assigned from weakSelf) then you basically need a place to store a "isDead" flag, which you set when the VC is dealloced. You then check this flag before doing anything with weakSelf/strongSelf. One solution is this:
You need a class who's only job is to store this "isDead" flag in a member variable. Something like a NSMutableBoolean. I won't write one out, it's simple enough, it just needs a single BOOL property.
In your VC's -[NSObject init] method you create an instance of this flag object, which should initially be set to false.
You then pass this object through to any block you queue for later execution, such that it is automatically retained/released by the block (i.e. without going through the weak/strong dance). Inside the block, you check if the flag is still NO before doing anything with weakSelf.
The key of course is to set the flag to YES inside the VC's -[NSObject dealloc] method, and then release it. If any blocks are still pending execution, they will have already retained the flag object themselves, and when they are later executed they will query the flag discover that the VC is now dead.
This sounds cumbersome (and it is, a bit) but the idea is that the "isDead" flag lives outside the scope of the VC and is therefore not tied to it's lifetime. As for thread safety, as long as you only set/query the flag inside the VC's init/dealloc method and when the block is executed (on the main thread, not on a background thread) it will be thread safe.
I don't want my VC to stay alive until the block eventually executes
But why does it matter? The memory of one VC shouldn't be that much, and if the block performs any UI actions on the VC, that's okay too, since the VC isn't displayed anyway.
Basically, the block should not capture a weak reference to the VC if the VC doesn't retain the block. Using non-zeroing weak reference means you guarantee that the object will stay alive, which is not the case here. I would suggest you not do this weak thing.
As I was walking through some line of codes I stumbled upon this problem a couple of days ago,
- (void)dealloc {
...
[self.postOfficeService deregister:self];
...
}
Where the de-registration from the Post Office Service is an asynchronous operation, even if it's not self evident from the interface as there's no block or function passed to the postOfficeService.
The internal implementation of postOfficeService's -deregister method is something like that
// -deregister:(id)formerSubscriber implementation
//some trivial checks here
// deregister former subscriber
dispatch_asynch(_serialQueue, ^{
[self.subcribers removeObject:formerSubscriber];
});
...
The container, self.subscribers, does perfectly its job and contains only weak references. I.e. it is a NSHashTable.
As long as the deregistration method got called within the dealloc method, I keep on getting a crash while postOfficeService is trying to remove the former subscribers from its list inside that asynch block, which is used for thread safety purposes I guess.
Adding a breakpoint on [self.subscribers removeObject:formerSubscriber], it's possible to notice that the formerSubscriber object is always a NSZombieObject. That's the reason for crashes.
I know that it's possible to get thread safety for deregister method without incurring in this problem - I figure it should be enough use the dispatch_synch in lieu of the dispatch_asynch version
I think this is one of the reason why asynchronous methods shouldn't be called within dealloc methods.
But the question is how's possible to constantly get NSZombie objects even if we are in an ARC environment and the container objects is a NSHashTable (so it should be working I guess)?
The rule is: When dealloc is called, the object will be gone once dealloc returns to its caller (whoever called release when the reference count was 0), and nothing is going to prevent this.
Before ARC, you might have tried to retain an object inside dealloc - doesn't help; once dealloc is called the object will go (and dealloc will be called only once for it, in case you do a retain / release inside dealloc). ARC does the same, just automatically.
Using ARC doesn't means all your memory problem magically disappeared.
What happened is
[obj release] called by ARC
[obj dealloc]
[obj.postOfficeService deregister:obj]
[obj retain] - sorry you can't cancel the deallocation process
dispatch_async
free(obj) - from this point, obj is a zombie
GCD scheduling tasks
dispatch_async execute task
use obj - crash
The correct solution is use dispatch_sync to make sure you not trying to use object after it is deallocated. (be careful about dead lock)
Don't call asynchronous cleanup methods from dealloc. It's just not a good idea. Your -deregister should be synchronous.
NSHashTable stores pointers - it's the equivalent of __unsafe_unretained or assign - UNLESS it was created using +weakObjectsHashTable or the equivalent set of options (NSHashTableZeroingWeakMemory and NSPointerFunctionsObjectPersonality). If it was not created that way, it is quite likely you will have values pointing to zombie objects.
The question of "why am I getting zombies" is best answered by profiling your application with the Zombies template in Instruments and stimulating the required behavior.
I agree with the others that you should probably avoid asynchronous cleanup in your -dealloc method. However, it may be possible to fix this by making the parameter to -deregister: __unsafe_unretained. That method would then have to treat the pointer purely as a opaque value. It must not dereference it or message it. Unfortunately, you don't control the implementation of NSHashTable and can't guarantee that. Even if NSHashTable could be relied upon, the interface of -removeObject: takes an implicitly strong object pointer, so ARC might retain the pointer when it's copied from the unsafe unretained pointer.
You might use the C function API for hash tables (e.g. NSHashRemove()) as suggested in the overview for the NSHashTable class.
I saw a solution to preventing block retain cycles here
But I am having trouble wrapping my head around why or even how it works.
In the example, a weak self reference is made and acted upon. I can see how this Breaks the cycle. However, within the block a strong reference is created. Wouldn't this recreate the retain cycle that we were trying to prevent in the first place?
Let's say for example that self is 0x123
Then weakself is also pointing to 0x123.
Then strongSelf would get set to 0x123 inside the block.
Wouldn't this make a retain cycle?(self has strong reference to block and strongSelf has a strong reference to self)
Let's say for example that self is 0x123 Then weakself is also pointing to 0x123. Then strongSelf would get set to 0x123 inside the block.
Wouldn't this make a retain cycle?
Actually, no; they do not all point directly at the same thing. The fact is that an ARC weak reference really does (behind the scenes) interpose an extra object into the mix. Let's call it a scratchpad object. It gives the illusion that we are pointing at the same object, but we are actually pointing through the scratchpad object, which does not retain the thing it points to. That's how ARC weak references work (also known as zeroing weak references); they involve special extra go-between scratchpad objects that can be retained without themselves retaining, thus breaking the chain.
In a retain cycle, A has a retain on B and B has a retain on A. Each will put a release on the other in its own dealloc, but that moment will never come.
In the block situation, A is self and B is the block. self put a retain on the block for whatever reason it did so (often rather obscure, having to do with copying, observers, etc.; it doesn't always happen, and in fact it happens much more rarely than most people seem to suppose). The block put a retain on self merely by virtue of the fact that it is a closure and self is referred to with in the block.
But by doing the weak-strong dance, we prevent this. What passes into the block is weakself,
which is actually a reference through the scratchpad object. The block can retain this, but it, in turn, does not retain self. Hence there is no retain cycle.
Within the block a strong reference is created. Wouldn't this recreate
the retain cycle that we were trying to prevent in the first place?
Yes, it does, but only temporarily. When strongSelf is initialized, it forms a strong reference to the current weakSelf value. (Or nil, if that object has been deallocated.) Once the block finishes running, this (local) strong reference will be released, breaking the cycle.
The problem is not a retain cycle per se (they happen all the time), but a long-lived retain cycle that keeps its objects alive for longer than expected.
Which of the following code section is correct?
Definition of correct for "me":
It should not have a retain cycle and so should not leak.
It must guarantee the running of method2 and method3. So MyObject variable in block must never ever be nil.(which may occur in __weak definitions...) (I do not want to check if it is nil to prevent crash. I always want it to be non-nil)
Update: (Additional Info)
Instruments tool did show strange leaks until I replace __block with __weak. However, after that I remembered that __weak references may disappear anytime. I have to be sure that it doesn't disappear and leak too. I don't have a timer. This someMethod is called on main thread when it observes a specific NSNotification.
#implementation MyObject...
-(void)someMethod{
AnotherObject *abc=[[AnotherObject alloc]init];
__weak MyObject *weakSelf=self;
abc.onSuccess=^{
__strong MyObject * strongSelf = weakSelf;
[strongSelf method2];
[strongSelf method3];
}
}
OR
#implementation MyObject...
-(void)someMethod{
AnotherObject *abc=[[AnotherObject alloc]init];
__block MyObject *blockSelf=self;
abc.onSuccess=^{
[blockSelf method2];
[blockSelf method3];
blockSelf=nil;
}
}
Update 2: Actual Code which always leaks if I don't use __weak:
__block RButton *_self=self;
_aimageView.onSuccess=^(void){
[_self.headerLabel setText:[_self.book title]];
_self = nil;
};
A block can mention self without causing any leak. So the first thing to do is specify the circumstances. Why do you think your block leaks? Have you checked? There's no point worrying if there is no reason to worry.
If the block's mentioning self does cause a leak, then the steps you can take depend upon the reason why that is happening. But you haven't shown us that. For example, a dispatch timer block that mentions self might cause a retain cycle, but when you are done with the timer you can break the cycle, so then there's no leak.
Thus it is better to understand a particular retain cycle than to program every block defensively when you don't have to.
EDIT (in response to your comment) This code from my book demonstrates various ways of handling memory management in an NSNotification observer situation: click here
I have looked at your actual code in "Update 2", but it's missing a lot of information. For example, is _aimageView an instance variable? local variable? or what? And what causes this onSuccess to be called?
If _aimageView is a local variable then I don't see evidence of a retain cycle.
My first thought is I don't understand why you want to guarantee that the code inside the body of the block runs in this example. It looks like the code inside is just updating some UI elements. Well, if the UI is no longer displaying (as when the current object has no references to it other than perhaps by this block), what's the point of updating the UI elements?
It's important to know what causes onSuccess to be called because different types of callbacks require different memory management architectures. For example, if the block is fired in response to a touch or some kind of event like that, then chances are that the object pointed to by self (which is probably some kind of view or view controller) must be still alive in order that the event occurs. If that's the case, then __weak will do what you want.
Basically, by the naming of these variables, it is reasonable to conclude that _aimageView is probably an image view that is conceptually "owned" by the current object, and the onSuccess is a completion block that is "owned" by the image view. Unless some other object has a strong reference to it, the "owned" object's lifetime is limited to its "owning" object's lifetime. Thus the block will not outlive the image view, which will not outlive the current object.
The only way that what you are afraid of (when the block runs, the object pointed to by self is deallocated) can happen, is if some other object stores a strong reference to _aimageView, or to the block. For the block, it's very unlikely that a "success block" of one object will be stored with other objects. For the image view, it's similarly unlikely if the image view "belongs" to the current object. (It's conceivable that it may be stored by the autorelease pool or something; but the autorelease pool will not call stuff on it except release, so that's not a problem.)
The only exception I can think of is if the image view is kept by a pending network operation or something, which when it is done calls the onSucess block. But if that were the case, I would say it's bad design to have a single object serve as both a view and a network operation. Rather, in that case, one should have a dedicated network operation object, which is a local variable, and set a completion block on it (e.g. store image in image view, set labels, etc.), start the operation, and not need to store the operation. Then the block can refer to self strongly, but there is no retain cycle.
To summarize, the block (and the image view that owns it) should fall into one of two categories, based on who keeps it alive (i.e. who retains it, who maintains a strong reference to it):
If self keeps it alive: e.g. A view controller keeps its views and subviews alive. In this case, it is safe to use __weak because the block does not exist outside of the life of self.
If someone else keeps it alive: e.g. an alert view -- you just create it and show it; the system maintains it on screen afterwards. In this case, self should not (and does not need to) have a reference to it, and it's okay to reference self strongly in the block; it won't cause a retain cycle.
Try to avoid situations where both self and someone else keep it alive.
As you require to guarantee that method2 and method3 are executed then you have a requirement that the object referenced by self stays alive. To do this you follow the standard pattern: keep a strong reference for as long as you need it. Your second code fragment does that.
Reference cycles are not inherently bad, it is unmanaged ones that are. Here you make a cycle and then break it, so there is no issue.
(Note as you can send a message to nil the first code fragment will not crash if strongSelf is nil - it just won't do anything.)
-(void)someMethod{
AnotherObject *abc=[[AnotherObject alloc]init];
abc.onSuccess=^{
[self method2];
[self method3];
}
}
This will make. It won't cause a retain cycle.