Memory management is being done manually, ARC is not used in this project..
The message object is created using alloc init and the code below is being called on background thread.
I pass a message object before the following call:
[self performSelectorOnMainThread:#selector(serverConnectionResult:) withObject: message waitUntilDone:NO];
After the call I want to do:
[message release];
I am confused whether I should do this, because I am concerned whether the message object will be always valid when serverConnectionResult is called? Is the method call performSelectorOnMainThread retaining the message object itself? What's the rule to know that the called method retains my passed object?
It is safe to do this. -performSelectorOnMainThread:withObject:waitUntilDone: will retain both the target of the message and the object. Similarly -performSelector:withObject:afterDelay: will also retain the target and the object.
You could also use Grand Central Dispatch and use dispatch_async on the main thread and pass in a block that calls your method and afterwards releases the message.
Related
Background
Our app has a class that attempts to implement the Receptionist Pattern for KVO observation. Other classes throughout the app (such as view controllers) create instances of this one Receptionist class to serve as the KVO observer. Each Receptionist instance keeps a copy of a block provided by the owner, which the Receptionist instance will invoke on the proper operation queue when a KVO notification arrives.
The Receptionist's dealloc method invokes the KVO removeObserver method. The Owner keeps the Receptionist instance as a strong-reference field, so when the Owner is deallocated, the Receptionist will remove itself as an observer in the process of being deallocated.
The Crash
We're seeing reports from the field of crashes when the KVO notification is received by a Receptionist instance on one thread while the same instance's dealloc is in progress on another thread. The Receptionist's implementation of observeValueForKeyPath:ofObject:change:context: is crashing on this line:
__weak typeof(self) weakSelf = self;
The stack trace in the crash report shows this as a call to objc_initWeak, which calls weak_register_no_lock, which calls _objc_fatal.
The object whose key is being observed by this particular Receptionist is never deallocated. The Owner is also not being deallocated; the Owner is replacing this Receptionist instance with a different one.
The Confusion
I can understand that it's not useful to create a weak reference to an object that's already being deallocated, but I would expect weakSelf to receive a nil value, not to cause a crash.
The documentation for objc_initWeak explicitly mentions setting the target to null if the argument to which the reference is desired has begun deallocation. That sounds like the desired behavior, but I don't think it's what I'm seeing. I'm not keen to replace that line with an explicit call to objc_initWeak, since I doubt I'd manage the deallocation properly.
Could it really be the Receptionist's responsibility to notice that its own deallocation is in progress before requesting a weak reference to self? I would assume that there's some window between when an NSObject's deallocation starts and when that object's dealloc method is called, so signalling within the object from the dealloc method sounds flaky.
Thank you for reading!
PS: heavily edited after reading the questions raised by Ken Thomases.
This has nothing to do with the creation of the weak reference. The line you cite should only be run in a context where something has a strong reference to self.
Think about it: the crash that you're seeing may be during that line within your observeValueForKeyPath:ofObject:change:context: implementation, but, since there's clearly a race between deallocation and the call of that method, the deallocation could also occur during dispatch of that method call (or some other point). You're vulnerable to different crashes. So, no changes to the implementation of the method could possibly fix the problem, since the problem could manifest before your method is even called.
It's your responsibility to keep a strong reference to an object if you're going to be calling methods on that object. Or, from the other perspective, to avoid calling methods on object pointers that you're not sure will live for the duration of the call (because you hold a strong reference or some other API guarantee).
With KVO, you need to remove observers before releasing your last strong reference.
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'm trying to figure out the best practice for memory management around invoking a delegate callback.
One issue I had seen in the past is that invoking a delegate callback may cause the object to be deallocated before returning, which may cause it to crash if the object tries to access its own properties after invoking the callback.
For example, an object (e.g. A) may do something like this:
- (void)doStuff
{
[_delegate done];
NSLog(#"msg = %#", _msg);
}
If invoking done leads to A getting deallocated, the subsequent attempt to access _msg will result in a BAD_ACCESS crash.
It is possible to get around this by, say, delaying the invocation of done till the next run loop (e.g. by doing a dispatch_async), but that would force us to have to make it asynchronous. Alternatively, we can retain self prior to calling done and releasing right after, but that just seems like a hacky workaround as well. Does any one have a recommended style for dealing with this issue?
I'm not sure this question really has anything to do with 'delegates' to be honest but more just memory management in general.
If you're not finished with an object make sure you are still 'retaining' it. When you're finished with it 'release' it and don't access it any further.
Also try and move to ARC if possible and life becomes much easier! :)
It's crashing because the delegate you want to call refers to a deallocated object.To fix this crash you need to set Delegate = nil; in your dealloc method.
You can not set property of delegate as retain as it will cause issue in memory management.
It shouldn't be possible, that the delegate method releases the sender. What let you run in this situation?
It is always possible to pair +1 with -1 methods in one (C) block.
If you work with MRC:
Anyway, I would prefer a retain + autorelease on sender in the delegate method before causing the deallocation over a retain + release in the delegate. Therefore the sender should be added as a parameter to the delegate method as usual:
- (void)senderIsDone:(Sender*)sender
{
[[sender retain] autorelease];
…
[sender release]; // Something like this in your code
}
But at all: This should not happen.
Another strategy is to delay that code that causes the deallocation. In the example above
- (void)senderIsDone:(Sender*)sender
{
[sender performSelectorOnMainThread:#selector( release ) withObject:nil waitUntilDone:NO]; // Whatever make the sender disappear
}
On iOS, if there is a single view app, and a new thread is created using:
[NSThread detachNewThreadSelector:#selector(consumeData:)
toTarget:self.consumer withObject:self.queue];
where the consumer is a Consumer object that will process data inside the method consumeData, and the queue is a Queue object, which is where the data comes from for the consumer to process.
But what if the thread needs to check whether a Switch on the main view is set to on or off? That is to toggle whether the Consumer object should do the work or pause at the moment. Should the withObject:self be used instead, so that the whole ViewController reference is passed to the thread, and then the thread will use viewController.view.______ to access the switch's value, and use viewController.queue to access the queue, or is there a better or alternative method?
Absolutely not. Nothing UI-related can ever be touched from another thread. It's simply not safe. If the other thread needs to know the switch's current value, then it needs to call back to the main thread before asking for it.
If you create a subclass, you could store your state in variables in the object, then access these variables from any thread; provided of course these operations do not call methods defined by UIKit.
I use RestKit with Object-mapping. This runs asyncronus and after receiving the Data from Server an object is updated.
The point is, i need to inform another class that a property of an object has changed.
Right now i run into an error:
bool _WebTryThreadLock(bool), 0x1ae420: Tried to obtain the web
lock from a thread other than the main thread or the web thread.
This may be a result of calling to UIKit from a secondary thread. Crashing now...
I tried to overwrite the setter for the property, but it looks like the property is set in another thread, not the main thread. calling a delegate there does not work.
What can i do to solve this?
Any help is appreciated!
Maybe you need send message like this:
[obj performSelectorOnMainThread:#selector(method)]