How to properly deal with a deallocated delegate of a queued nsoperation - objective-c

In my current project, several view controllers (like vc) spawn NSOperation objects (like operation) that are executed on a static NSOperationQueue. While the operation is waiting or running, it will report back to the view controller via delegation (operation.delegate = vc, assigned not retained).
These operations can take a while though, and in the mean time the app can dealloc the view controller (by popping them of a navigation controller's stack).
So far everything is intentional. The class containing the static NSOperationQueue has a way to get back at the operations, therefore the view controllers do not retain them. They're just alloc/init/autoreleased and put on the queue.
Now this also causes the problem. After the view controller deallocates, any calls to the NSOperation's spirited delegate will cause a bad access violation. From what I understand, it is not possible to check whether an object at a pointer has been deallocated, as stated in this question.
One fix I can think of is retaining the operation and setting the operation.delegate to nil on dealloc. But that'd be my least popular fix, for it would introduce a lot of extra ivars/properties to keep track of.
My question therefore is, are there other ways to work around this problem and if so, could you sketch one here?
Cheers,
EP.
SOLUTION: The approach that worked out best for me was a slight variation to Guiliano's answer:
Implementing every delegate protocol in the queue manager is not feasible (20+ different protocols with 50+ methods), so I kept the direct delegate assignments. What I did change was the class making the assign call. This used to be the class (and delegate) that created the request, but now it is offloaded to the queue manager.
The queue manager, next to assigning the delegate to the operation, also holds a secondary mutable dictionary to keep track of the delegate/operation pairs.
Every delegate instance calls a [QueueManager invalidateDelegate:self] method on deallocation, which then looks up the request that belonged to the delegate and nils it. The dictionary operation/delegate pair is then also deleted to allow proper deallocation of the operation.
Lastly with KVO observing the isFinished property of each operation, the mutable dict is kept clean, to make sure that all operation retain counts actually deallocate after they're finished.
Thanks Guiliano for providing the hint to using KVO for cracking this!

I would suggest to review your architecture and move the delegate to the class (assume QueueManager) that manages the queue instead of having a delegate in each operation:
Create a QueueManagerDelegate that
implements the method(s) you need to
notify the viewControllers
In QueueManager add a KVO observer for the isFinished property of each NSOperation (do this before adding the operation to the queue ;))
In the callback of the KVO call the delegate method(s) you need only if delegate is != nil
Add an invalidate method to QueueManager and call this method in the dealloc method of your UIViewController(s)
-(void)invalidate
{
self->delegate = nil;
}
in case you need a refresh on KVO: Kvo programming guide

The best advice here is to review the architecture of the app to avoid such situations. However, if there current code can't be changed some-why, you can use NSNotificationCenter. Every time your view controller is deallocated you can post a notification, this notifications must be caught by the NSOperationQueue holder, simple foreach cycle in the notification handler to nil the delegate for a deallocated view controller. Should do the trick.

You should also be checking to ensure that any delegates, if non-nil, are also able to respond to a message from the operation completion. You do this using the respondsToSelector function that all NSObject subclasses posess.
In my projects, I've abstracted this checking into a category on NSObject that lets me safely call delegates with an arbitrary number of object arguments:
- (void) dispatchSelector:(SEL)selector
target:(id)target
objects:(NSArray*)objects
onMainThread:(BOOL)onMainThread {
if(target && [target respondsToSelector:selector]) {
// Do your delegate calls here as you please
}
}
You can see the full example here: https://github.com/chaione/ChaiOneUtils/blob/master/Categories/NSObject-Dispatch.m

Related

An object, working asynchronously, disappears because there is no reference to it. Is this expected?

I am new to Objective-C and I just wanted to confirm whether what I'm observing is correct.
This is what's going on:
Class A creates a CLLocationManager object
A is set as the delegate for the CLLocationManager object
Location services are activated (in order to have the CLLocationManager object call A with location updates)
However, location updates are not received unless a reference to the CLLocationManager object is kept somewhere.
I assume that this is because of ARC. But I am asking because my expectation was that CLLocationManager would not be deallocated: it has work to do, and the delegate methods receive a reference to the CLLocationManager, so why keep an extra property!
Is this interpretation correct?
Is there anything else one can do to keep the CLLocationManager object working other than have a property pointing to it?
As an experiment I tried running the steps above within a dispatch queue, but the location updates were not received.
Thanks!
I guess your big question is whether this behavior is common and expected.
The answer is the following - it depends. It is an implementation decision. I've seen frameworks working both ways. Usually, you can guess the behavior from the method naming.
For example, a new instance of CLLocationManager has to be created before use. Once you have to call alloc and init, you should always keep the reference.
On the other hand, in frameworks where you don't create a new object, you don't need to hold the reference - for example [NSNotificationCenter defaultCenter] or [UIApplication sharedApplication]. The framework holds the reference for you.
Frameworks which use class methods (e.g. STTwitter) also typically hold the references for you.
Your following assumption
and the delegate methods receive a reference to the CLLocationManager
is wrong. The delegate is only a set of methods. It doesn't hold anything if you don't implement it explicitly. Note that you have to also keep a reference to the delegate. The manager won't keep it alive.
If you are not keeping a reference to the CLLocationManager object it will be deallocated once you reach the end of the scope

How should delegator object lifespans be extended for the duration of a delegate callback?

I'm going to distill a very common situation to a general form. Say I'm building some library object that goes off, does some async work, then calls back to a delegate method when it's done. Now, also say for some arbitrary reason that I can't use ARC or blocks for the callback. It's old-fashioned. Let's call this object the Worker.
Now say there are several other classes across various apps that don't know anything about Worker aside from its public interface. They use the Workers to their own ends. Let's call these classes the Consumers.
Say Worker makes delegate callbacks like this:
// "Private" method called internally when work is done.
- (void)somethingFinished
{
[self.delegate workerDidFinish];
[self someTask];
}
And say some particular Consumer handles the callback like this:
- (void)workerDidFinish
{
// Assume "worker" is a retain property to a Worker
// instance that we previously created and began working,
// and that it's also the sender of the message.
self.worker = nil;
// Other code...
}
Now, if nothing else has retained that particular Worker instance, we're in trouble. The Worker will be deallocated, then control will return to its -somethingFinished method, where it will then send -someTask to reclaimed or garbage memory, and likely crash. Nobody has blatantly violated any memory management rules.
I'm not asking for a technical solution here. I know several options. I'm wondering on whom the burden of responsibility for the fix falls. Should I, as the component designer, implement Worker's -somethingFinished method such that the Worker's lifespan is extended for the duration of the method with something like [[self retain] autorelease] at the beginning? Or should I, as the consumer of the component be aware that I might be blowing away an object halfway through an instance method, and wait until later to release it?
This question's answers all seem to indicate that releasing the object from the callback is a Bad Idea. Unfortunately, there's a lot of distracting information in that question about how exactly the Worker (CLLocationManager in this instance) is passed to the delegate that I've intentionally avoided here. This question is suffering the same scenario, and offers the other solution.
Personally, I can't see how the Consumer could be held responsible. It's not breaking any memory management rules, and is politely consuming the Worker's public interface. It's simply releasing an instance when it's no longer needed. But on the other hand, does that mean that any object that could possibly, somehow, be deallocated mid-method needs to artificially extend its own lifespan? Delegate methods, after all, aren't the only way a message sender could end up deallocated in the middle of a method.
So ultimately, who is responsible for the fix? Worker? Consumer? Can it be determined canonically?
I think the burden is on the Worker in this example. The problem I see is that the Worker object is doing something internally after telling its Consumer that its work has finished. The Worker only exists for the sake of the Consumer so if the Consumer's objective is met why is Worker still doing something that has no value to the Consumer? If there are internal tasks that need to be completed after the 'consumable' work is done then those tasks are not suitably placed in an instance of the Worker object, but probably should be done by another internal object owned by a less volatile library class that won't be dealloc'd by actions of the Consumer.
When the delegate gets notified, should start observing the worker and release it only when somethingFinushed method is terminated.

iOS: Releasing Object Thats In Use (ARC)

I have an object that I have created that subscribes to some NSNotificationCenter notifications, but it is being released, since I don't have a pointer to the instantiation. The instantiation has a delegate that will call a method back to another class I have, so I felt it unnecessary to also have a pointer to it, since the pointer doesn't serve any purpose.
Basically, I have an instantiation of a class, DelegateListener (name is just for example purposes), which subscribes to some of the default NSNotificationCeneter's notifications. This instantiation isn't assigned to any pointer after the instantiation ends. The instantiation, however, has a property, delegate. I assign a value to that delegate during the instantiation. That delegate implements methods that I want the DelegateListener to call when the notifications that it subscribed to fire.
The problem with this is that the instantiation of DelegateListener that I create gets released, unless if I assign it to a retained pointer. Is there a way to avoid this automatic release of my DelegateListener instantiation within ARC?
You should store the DelegateListener instance in a static variable if you don't have multiple of them.
I felt it unnecessary to also have a pointer to it, since the pointer doesn't serve any purpose.
The pointer does (or at least should) serve a purpose: when you no longer need to listen to the notifications, you should unsubscribe the DelegateListener from receiving them. At the very least, this should happen when your application goes to the background (unless the point of it is to perform background processing), and when your application terminates.
[[NSNotificationCenter defaultCenter] removeObserver:delegateListener];

How to manage unsafe_unretained ivars/properties?

I started objective-c and iOS a couple of weeks ago (worth bearing in mind), and I apologise in advance for the awful diagram!!
The above diagram shows the structure of my calls to a webservice. Thin arrows denote an object creating another object, whereas thick arrows denote an object holding a strong (retained) reference to the pointed-to object.
I believe that this contains what is called a "circular reference" and will create problems when it comes to deallocating the objects.
I understand that the easy answer would be to replace some of the strong references to weak ones, which I'd love to do, except my project is also targeting iOS 3.2 (not my decision - I can't really change this fact!). So, I think I'm right in saying that I have to use __unsafe_unretained instead, but I'm quite worried about the fact that these won't auto-zero, as I'll end up with EXC_BAD_ACCESS problems when objects get deallocated...
So my problem is firstly that I have circular references. To solve, I would have to use __unsafe_unretained, which leads to my second problem: How to correctly manage these?
A question that might be related is: How does NSURLConnection manage it's strong references? I have heard from various sources that it retains its delegate? So...if I retain an NSURLConnection, (and am also its delegate) and it retains me, this would also be a circular reference, no? How does it get around my problem?
Any advice is very welcome!
Regards,
Nick
When a parent has a reference to a child object, it should use a strong reference. When a child has a reference to it's parent object, it should use a weak reference, aka unsafe_unretained.
By convention, delegate relationships in iOS are usually weak references, so you'll find that most delegate properties on Apple's own classes are declared as unsafe_unretained.
So your controller retains the services that it is using, but the services only weakly link back to the controller. That way, if the controller is released, the whole lot can be safely disposed of without any circular references.
The danger with this is that if the web service is doing some long-running task, and the controller gets released before it has finished, the service is left with a dangling pointer to it's now-deallocated delegate. If it tries to send a message to the delegate, such as "I have finished" it will crash.
There are a few approaches to help solve this (they aren't mutually exclusive - you should try to do them all whenever possible):
1) Always set the delegate properties of your services to nil in your controller's dealloc method. This ensures that when the controller is released, the delegate references to it are set to nil (sort of a crude, manual equivalent of what ARC's weak references do automatically).
2) When creating your own service classes that have delegates, make them retain their delegate while they are running and then release the delegate when they are done. That way the delegate object can't get deallocated while the service is still sending it messages, but it will still get released once the service has finished (NSTimer's and NSURLConnections both work this way - they retain their delegate while they are running and release it when they are done).
3) Try not to have long-running services owned by something transient like a view controller. Consider creating singleton objects (shared static object instances) that own your services, that way the service can do it's job in the background regardless of what's going on in the view layer. The controller can still call the service, but doesn't own it - the service is owned by a static object that will exist for the duration that the app is running, and so there's no risk of leaks or premature releases. The service can communicate with the controller via NSNotifications instead of delegate calls, so there is no need for it to have a reference to an object that may vanish. NSNotifications are a great way to communicate between multiple classes without creating circular references.
All of your questions and concerns are correct, and this problem with the previous use of assign (now better named __unsafe_unretained) is why Apple developed auto-zeroing for weak. But we've dealt reasonably safely with assign delegates for many years, so as you suspect, there are ways to do it.
First, as a matter of practice, you should always clear yourself as the delegate when your release an object you were delegate for. Pre-ARC, this was traditionally done in dealloc:
- (void)dealloc {
[tableView_ setDelegate:nil];
[tableView_ release];
tableView_ = nil;
}
You should still include that setDelegate:nil in your dealloc if delegate is __unsafe_unretained. This will address the most common form of the problem (when the delegate is deallocated before the delegating object).
Regarding NSURLConnection, you are also correct that it retains its delegate. This is ok because it has a lifespan typically much shorter than its delegate (versus a table view delegate which almost always has the same lifespan as the table view). See " How to work around/handle delegation EXC_BAD_ACCESS errors? Obj C " for more discussion on this in a pre-ARC context (the same concepts apply in the new world of strong/weak).

Can I start a thread by pressing a button in a cocoa interface and keep using interface while thread runs?

I have a Cocoa interface. When I press a button I want to process some data, but I want to keep using the interface while it's working. I guess the only solution is NSThread. Now will there be a locking mechanism preventing me from returning from an IBAction method if it spawns a thread?
No, there is no locking mechanism. The new thread will start and the current thread will continue. You may want to look at performSelectorInBackground:withObject: and possibly NSOperation in addition to NSThread.
Take a look at NSOperation. NSOperation is one of the few cocoa classes which must be subclassed for it to be useful. By adding a delegate property to your NSOperation subclass you can get notified when the operation completes. Also, you can add a userInfo property to allow the operation to pass back arbitary data to the delegate
#implementation MyNSOperationSubclass
-(void)main
{
//do operation here
//operationResult is used to report back to the delegate. operationResult could include a userInfo key so that the delegate can have some data passed back, or an error key to indicate success of the operation.
NSDictionary *operationResult;
//Some checks to ensure that the delegate implements operationHasFinished: should be added.
//waitUntilDone: YES locks the main thread
[[self delegate] performSelectorOnMainThread:#selector(operationHasFinished:) withObject:operationResult waitUntilDone: YES];
}
#end
Don't know much about cocoa, but starting a new thread for processing something in background while keeping the UI thread free for taking user inputs(and thus preventing UI from freezing) is the most widely used technique for the problem you have mentioned.Both threads will work together (concurrently) and the programmer has to take care of the synchronization issues if any.You should go ahead with the technique without doubt.
Thanks,
Sourabh