I create an NSOperation every time my app launches or resigns active. I need to queue them with dependencies such that two never execute at the same time, but one after another.
Is it safe to do this?
Hold a strong reference to the NSOperation object in the App Delegate.
When the app resigns active, simply check if hat property is not nil.
If it is not nil, check if the current NSOperation -isFinished.
If it's finished, just add the new one to the queue.
If it's not finished yet, create the new one and set a dependency on the running one, then add it to the queue.
I'm concerned a bit with multithreading issues here. The documentation of the -isFinished or -addDependency: methods doesn't say they should not be called from the main thread. So I guess it is ok to do that.
Edit: The NSOperation performs some file system operations in the background.
If you want to ensure they are not called at the same time, set the maximumConcurrentOperationCount: on your NSOperationQueue to 1.
- (void)setMaxConcurrentOperationCount:(NSInteger)count
This assumes you are putting both of your NSOperations in the same queue.
In response to your other questions. I'm not sure what you are doing - but yes you can hold strong a reference to your NSOperation on the AppDelegate if you want, and you can check isFinished
Related
Let's say I have view controller A and view controller B.
In VC A, I push VC B. Then in VC B, I execute some background tasks using NSOperation. In the background tasks, I modify VC B's variables.
What happens if the background tasks are not finished and I quit VC B? Will the operations be cancelled or will they still be executing? When debugging, it seems like they are still executing. In that case, wouldn't they be accessing already released variables (since I quitted VC B).
I'm a bit confused by this, anyone can clear me up? :)
Thanks,
You are correct, the operation does not magically disappear just because the object that spawned it did.
You will cause the OS to throw an exception as it tries to access the now deallocated view controller object. This is the danger of doing background threaded operations.
You need to plan accordingly, in this case, be able to cancel your operation when VC B gets deallocated. This means subclassing the NSOperation, implementing main() and checking for isCancelled.
See Apple's documentation regarding NSOperation, NSOperationQueues, and Concurrency Programming.
It would be good to consider the purpose of VC-B vs the purpose of the background activities. If the background activities are there to support what the user sees on VC-B, and when user moves away from VC-B the background activities are no longer relevant, then leaving VC-B should cause the background activities to cease. On the other hand if the background activities have a purpose 'larger than' VC-B, the user would expect them to continue; in this case, is's probably appropriate for some 'relatively permanent / long-lived' object (a 'background manager') to manage the activities. In the latter case, the VC's would interact with the background manager as appropriate.
So (as it should be) it comes down to what do you (and more importantly, what does the user) want/expect...
From the docs:
Once you add an operation to a queue, the operation is out of your hands. The queue takes over and handles the scheduling of that task.
Ideally you shouldn't modify your VC variables directly if it can be deallocated while the operation is running, but should compute a result and then do a callback. If you are using ARC you can keep a weak reference to your view controller and it will be safe to reference even if your VC gets deallocated.
If you're looking to implement concurrency you might want to look into using Grand Central Dispatch and blocks. This would work better as blocks encapsulate and retain as needed any variables you reference inside the block, are much easier to set up and execute and make cleaner code.
I have an NSOperation running not in the main thread. It is spawned from a UITableViewController. When the operation is complete, I'd like to reload the tableview since some data has changed. I've set a delegate for the background to notify on completion. When done, I call a wrapper around reloadData specifically on the main thread using performSelectorOnMainThread.
For the most part, this works well, however, there is a non-0 chance that the original (edit)tableViewController (/edit) gets released and I get zombie calls.
So the question is in 2 parts:
Is it possible to have a delegate from the background thread without retaining the object?
Is this just a bad design? Should I be using NSNotifications instead? Would that be the preferred method of notifying in this case?
Thanks in advance.
A delegate should be retained if there is a possibility that it might be released before any operation on the delegate is invoked. You can set up a state in tableViewController to handle the case when the delegate callback is invoked and the tableViewController is not to be used (Basically make the callbacks act as no-op). Once your operation is done, just release the delegate object.
It is not a bad design but you just need to handle these conditions.
I am writing an app for the iPhone and arrived at this situation.
I have a view controller, myViewController, that will dealloc whenever the user taps the "back" button on the screen. There a thread in the background that communicates with a remote server and may message a method, updateUI, method in myViewController.
What would happen if the background thread messages updateUI in myViewController, but the user just happened to tapped the "back" button at the right time such that it causes myViewController to dealloc while updateUI is still executing?
My guess is that the dealloc method will run and the app might crash if updateUI ends up using a null pointer. Assuming this is the case, the current solution I have is:
[self retain];
// updateUI code here
[self release];
I am unsure if this is the best solution, as I feel that this is a common problem when dealing with multiple threads.
Is my assumption correct? If so, is there a better solution?
What you are describing is known as a "race condition." Race conditions can be difficult to identify in testing, track down once reported, and reproduce because sometimes execution in the debugger can effectively modify how the code is being executed (avoiding the condition that one is trying to reproduce). Race conditions are one of the major pitfalls in concurrent programming - making the area deceptively difficult to do well.
In principle, it is a best practice to minimize the use of shared resources and closely qualify how the sharing is coordinated when implementing concurrency. If an object is shared across multiple threads, it should be retained by each of them to ensure that the object stays in-scope while each thread completes its processing.
Apple has been taking steps to simplify implementing concurrency. This is a good starting point for familiarizing yourself with the topic on iOS.
http://developer.apple.com/library/ios/#documentation/General/Conceptual/ConcurrencyProgrammingGuide/Introduction/Introduction.html%23//apple_ref/doc/uid/TP40008091
It's also useful to be aware that Objective-C 2.0's properties can support atomic operations (and are atomic by default, thus the nonatomic keyword to disable this default).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocProperties.html
And, this is the old-school guide to threads (out of favor approach, but still useful background - be sure to be familiar with NSLock).
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html%23//apple_ref/doc/uid/10000057i
Whenever some part of your code depends on another, it should retain this dependency until it is not needed. In your case the background worker should retain the controller and only release it when the work is done (or cancelled).
If you dealloc and then nil your objects then this shouldn't be an issue - you can send messages to nil in objective-c.
Alternatively, if you wanted the viewController to get the message and if your targeting iOs 4, you can use blocks and GCD. Blocks auto retain objects and therefore if a block references your viewController, would keep it around for as long as its needed, even if -(void)dealloc ; has been called.
Here is a decent Block tutorial
Yes, your app will crash, likely with something along the lines of EXC_BAD_ACCESS.
As far as multithreading, you will want to retain your objects until everything is done with them and program defensively. Check for the existence of objects before trying to manipulate them.
I wrote some sample code in a separate application, that basically queues up items in an NSMutableArray. I then have a method which loops through that array and creates an NSInvocationOperation, puts it in the NSOperationQueue, releases the operation and so forth. The method then gets called (simply prints out the string that was stored in the array and passed into the operation), does it's job, and sends an NSNotification back to the ViewController. The observer method gets hit, but the operations have a lag on them. For instance, the observer method simply updates a UILabel with how many messages are left in the queue. It eventually does this, but there seems to be a five second lag in between all of the NSOperations completing and the UI updating. To me it seems like the NSOperationQueue is blocking the main thread. Is there anyway to get the UI to respond immediately to the notifications?
One important note is that I have not tested this on the phone yet, just the simulator. I'm not sure if this makes a difference.
Sorry in advance. I'm away from my computer and I don't have the code in front of me. Hopefully I explained it well enough. Also I have read the documentation, just haven't found anything that's really answering this specific question for me.
The delay is typical of UI updates that are performed on threads other than main.
To see your update instantly on the UILabel, be sure to invoke whatever method is updating the label's text as follows:
[self performSelectorOnMainThread:#(myMethodToUpdateLabelWithText:) withObject:text waitUntilDone:NO];
where myMethodToUpdateLabelWithText is a method within your class that sets the label's text value.
According to the rules of memory management in a non garbage collected world, one is not supposed to retain a the calling object in a delegate. Scenario goes like this:
I have a class that inherits from UITableViewController and contains a search bar. I run expensive search operations in a secondary thread. This is all done with an NSOperationQueue and subclasses NSOperation instances. I pass the controller as a delegate that adheres to a callback protocol into the NSOperation.
There are edge cases when the application crashes because once an item is selected from the UITableViewController, I dismiss it and thus its retain count goes to 0 and dealloc gets invoked on it. The delegate didn't get to send its message in time as the results are being passed at about the same time the dealloc happens.
Should I design this differently? Should I call retain on my controller from the delegate to ensure it exists until the NSOperation itself is dealloc'd? Will this cause a memory leak? Right now if I put a retain on the controller, the crashes goes away. I don't want to leak memory though and need to understand if there are cases where retaining the delegate makes sense.
Just to recap.
UITableViewController creates an NSOperationQueue and NSOperation that gets embedded into the queue. The UITableViewController passes itself as a delegate to NSOperation. NSOperation calls a method on UITableViewController when it's ready. If I retain the UITableViewController, I guarantee it's there, but I'm not sure if I'm leaking memory. If I only use an assign property, edge cases occur where the UITableViewController gets dealloc'd and objc_msgSend() gets called on an object that doesn't exist in memory and a crash is imminent.
In the general case, a delegate owns the objects it has set itself as the delegate to, or at least retains references to them directly or indirectly. In the dealloc method, a delegate should either release all objects that it is the delegate of in such a way that prevents future callbacks, like NSTimer invalidate, or clear the delegate member of those objects that may persist.
While it is only convention that prevents retaining a delegate, it is convention based on good design. In your case, wouldn't the results be discarded anyway since the delegate is being disposed?
You can make the delegate property of your NSOperation atomic by not setting the nonatomic flag and synthesizing the getter and setter. Or you can use performSelectorOnMainThread before using the delegate member.
To recap, there is usually a better solution than retaining the delegate.
I really wonder though if the "don't retain your delegate" rules still apply specifically to multi-threaded object/delegate relationships, especially the one you're writing about. I found my way to this question because I'm in exactly the same situation: I'm running a finite but unpredictable length asynchronous network operation (it will finish and self-terminate "sometime soon") in an NSOperation, and using a delegate stored in the NSOperation to notify completion of the operation back to the original requestor object.
Retaining the delegate in this case makes perfect sense to me. The operation will complete eventually, it will call the "done" method on the delegate, and then will release the delegate and self-terminate. If the calling object would have been dealloc'd while the NSOperation was running, it just sticks around a little longer until the operation completes.
Note that marking the delegate property atomic as drawnonward suggests is in itself not sufficient to protect against race conditions! Setting a property as atomic only means that a reader or writer of the property will write or read an entire whole value at a time, nothing else. The following sequence would result in a crash:
(NSOperation thread): NSOperation has finished and is preparing to notify the delegate object . The code atomically reads the value of the delegate property preparing to call its "done" method.
----> Thread switch to main
(main thread) the requesting object is dealloc'd, and in -(void)dealloc (atomically!) sets the NSOperation's delegate property to nil, dealloc finishes, and the object is now gone
-----> Thread switch to NSOperation thread
(NSOperation thread) calls [delegate allDone] and the program crashes (if you're lucky) because the delegate object is gone. If you're lucky. Maybe something else was allocated in that space in the meantime and now you have unpredictable corruption, what fun!
The key thing here is that the retain cycle is temporary by its very nature -- the NSOperation will complete and clean up all on it own. It's not like a UITextField and a UIViewController holding retained references to each other that will never go away and thus leak the memory.
It seems to me that retaining the delegate in the case of an asynchronous, limited, self-terminating operation is the cleanest and most robust implementation.
EDIT:
if you absolutely must not retain the delegate because it causes memory problems, then instead the delegate object and the operation object must both use an explicit locking mechanism to synchronize access to the delegate pointer stored in the operation. "atomic" properties do not provide a thread-safe protocol to marking the delegate pointer nil.
But I think this gets really complicated and full of race conditions. I think at the very least that you must in the delegate object's dealloc, run a locking protocol to make sure that the operation's delegate pointer is safely set to nil so that arbitrary thread interleaving cannot under any circumstances call to a dealloc'd delegate object.
Slavish adherence to rules sometimes makes things way more complicated than they need to be. It's best to understand what the rules are, why they are there, and so you know when it makes sense (like I believe it does in this very particular scenario) not to follow them, and what the advantages/disadvantages are of doing so.
I approach this a little differently, and I do not retain delegates.
If I need to lose the view, I cancel the NSOperation. I believe that its good design that if a thread's return value has nowhere to go, then the thread should stop.
I also have seen the edge case where the thread cannot be stopped. In this case, I nil the unretained delegate, just to be sure, and check that the delegate is not nil before making the callback.
Holding on to an object, even temporarily, when its no longer needed, especially on the iPhone chews up memory and, in my opinion, is bad design.
As per #Bogatyr's answer, a simple check for nil before calling [delegate allDone] is much cleaner.
You could retain the UITableViewController at the beginning of the NSOperation and release it at its end.
Alternatively, you could set it to nil after it is released so the dangling call from the NSOperation won't crash your program.
Depending on how the NSOperation is executed you could also autorelease the UITableViewController instead of releasing it, so the NSOperation can still use it.
However, all these things really only cure the symptoms, not the illness. The correct way to do it is outlined by drawnonward.