Threadsafe UITableView - cocoa

I'm using a UITableView to show some data from an array. This array can be changed at any time by other threads. (I believe that whether the array is mutable, or just replaced entirely, doesn't make a difference.) Access to the array itself is threadsafe.
What's the proper way to ensure thread safety with regard to the tableview? I'm worried, for example, that I might change the array to be shorter just before cellForRowAtIndexPath is called, leading to an NSRangeException.
Should I...
Enforce that the array is only changed on the main thread? (Seems ugly.)
Maintain a shadow array and update this on the main thread through KVO observing?
??? There must be a better solution...

From your description, you really have TWO different data sets:
The actual data, as it exists in your model
The data that's displayed to the user
Thus, you already have, in effect, a 'shadow' array (virtual shadow, which may be stretching the metaphor too far). I'd say your best bet is to formalize this arrangement, and keep a 'display' array that only gets modified in the main thread. In it, you can have objects from your 'real' array; since they're only pointers, you won't be giving up too much memory.
Threading is evil.

Without knowing more about your app, I think one of the better solutions would be to keep the array on the main thread and dispatch back to it whenever another thread needs to make a change. Like this:
dispatch_async(dispatch_get_main_queue(), ^{
[array addObject:object];
[tableView reloadData];
});
Of course, you can get much more complex with the dispatch API, but it does handle locking and everything for you. Definitely more elegant than using an NSLock. It does only work on iOS 4 or later though.

I have the same situation. I have an array of C++ objects that provide the info in the tableview.
As Ben referred to, I also have a temporary "shadow" array into which new information is downloaded over the Internet. When the query is complete, I reconcile that array with the one backing the tableview, which is quite fast. The question is how to protect the array during that reconciliation.
I'm doing the reconciliation on the main thread, but I'm not sure that's sufficient to protect from conflicts, especially if the user taps on an entry while there's a query pending; the underlying object he's viewing details for might be blown away.
There's a similar question to yours (and mine) here:
Update UITableView using threads
but the answers berate the poster for taking too long with his background operation, instead of answering his question.
I'm going to use an NSLock, and acquire it before altering the array and in all of the UITableView delegate methods, unless somebody has a better idea.

Related

Dealing with NSManagedObjects when using MVVM

I've been struggling with a good answer to this one for a while:
How do you deal with NSManagedObjects as the Model in MVVM?
I've tried a few different approaches:
Retaining a copy of the NSManagedObject on the ViewModel — although this seems a bit dangerous to me, with the potential for threading issues
Unpacking the NSManagedObject into the properties I actually use on the VM via an -initWithModel: method — this would mean I no longer receive any updates to the NSManagedObject after the initial initialisation
Retaining a copy of the NSManagedObject's NSManagedObjectID, and using an NSManagedObjectContext specifically for each ViewModel instance to retrieve and monitor a private NSManagedObject and it's attributes — this seems a bit heavy to do this for every ViewModel instance (and potentially quite fragile)
None of these seem ideal. I have an idea in my head that it might be best to combine passing in an initial NSManagedObject instance via -initWithModel:, but only retaining the NSManagedObjectID, then listening for core data save notifications and weeding out any that don't relate to the retained object ID.
What I would do, and I don't know if this is necessarily the best practice, is pass in the model and then bind properties of the view model to (possibly mapped) properties on the model. That way you get updates through the view model. It does leave a little bit open to threading problems, but you can use deliverOn: to ensure that updates are always delivered on the main scheduler.

Cocoa: Finding the missing reference for deallocating

I'm almost done with and app and I'm using instruments to analyse it. I'm having a problem with ARC deallocating something, but I don't know what. I run instruments using the allocations tool ,what I'm doing is starting the app at the main view, then I mark a heap, I interact with the app a little and return to the original main view and mark another heap.
I do this several times and as I understand it, there should not be any significant heap growth because I am returning to the exact same place, everything I did in between should have been deallocated, providing no heap growth. However I have significant growth so I dive into the heaps and I find that almost everything on it has a retain count of 1, which leads me to believe that one object or view, etc is not being deallocated because of a mistake I've made and that object is what's holding references to everything else.
What I'm trying to find out is which object is not being deallocated. Instruments is very vague and only offers obscure pointers that do not allow me to trace back the problem.
Please let me know if there is a way for me to trace what is holding a reference that may be keeping the retain count at 1.
Thanks.
My 1st thought are 2 things:
1) You may have a retain cycle: As an example, one object has to a delegate a strong reference. And the delegate has also a strong reference (instead of a weak reference) to the 1st object back. Since both of them "hold" the other one, none of them can be released.
2) You may have a multi-threaded app, one of the threads does not have an autorelease pool assigned (i.e. does not have an #autoreleasepool block), and is creating autorelease objects. This may happen even in a simple getter method that returns an autorelease object. If so, the autorelease object is "put" into an non-existing autorelease pool (which does not give you an error message, since you can send any message to nil), and it is never released.
Maybe one of these cases applies to your problem.

KVO observation vs NSNotificationCenter observation

I'm wondering if there is a reason to use one over the other in KVO vs NSNotificationCenter observations. Performance, memory usage, speed, etc?
The two are not always interchangeable. Conceptually, KVO is only for observing a property of an object. For example, you can't use KVO to replace NSApplicationWillTerminateNotification because it notifies observers about an event happening, not a change in a property of an object.
As for performance and memory usage, they are both fast and use negligible memory. NSNotificationQueue has coalescing to stop floods of notifications. KVO doesn't have any coalescing as far as I know, which did cause performance issues for me at one point. I was observing hundreds of objects, and when a batch update happened to those objects I'd get hundreds of KVO callbacks. It wasn't a performance issue with KVO itself, but with my own code running as a result of the batch update.
Performance isn't really the issue, it's more about the best fit for the problem. If it's a property change, use KVO. If it's not a property change, use a delegate or a notification depending on whether you need a single observer or multiple observers.
A very old question, but thought of adding some points. I agree with Tom Dalling's answer, however, there are many scenarios in big applications where we tend to add observer for a property of an object and we cannot, or, we miss out removing them from list of observers.
Let us consider the following scenario from my application - A ViewController displays a snake object, I am observing for a property change on this object - "venom". So whenever viewController needed to show a different snake I would simply remove the view controller from observer of that snake object.
The app evolved to show a list of snakes instead of a single snake, this means I had to observe for property of all snakes in that object. Now, when an old snake is removed from the array I should get to know about this event so that I can remove view controller as observer from this snake object. To do this, I have to first observe for changes on the array itself. To do this I have to follow particular protocol to insert objects into array and remove them from array. This way the complexity builds on. We all do know the consequences of not removing observer from an object and if that object is released by the OS!
Above is just one example to cite, the main problem here is I cannot get the list of KVO observers for a given object to remove them from observers before this object gets released - This can be easily achieved by NSNotification and NSNotificationCenter. At times, I tend to be inclined towards using NSNotification over KVO, however, KVO always has edge over notification in terms of good design practice.

Core Data and threading

What are some of the obscure pitfalls of using Core Data and threads? I've read much of the documentation, and so far I've come across the following either in the docs or through painful experience:
Use a new NSManagedObjectContext for each thread, but a single NSPersistentStoreCoordinator is enough for the whole app.
Before sending an NSManagedObject's objectID back to the main thread (or any other thread), be sure the context has been saved (or at a minimum, it wasn't a newly-inserted-but-not-yet-saved object) - otherwise the objectID will actually be a temporary ID and not a persistent one.
Use mergeChangesFromContextDidSaveNotification: to detect when a save happens in another thread and use that to merge those changes with the current thread's context.
Bonus question/observation: I was led to believe by the wording of some of the docs that mergeChangesFromContextDidSaveNotification: is something only needed by the main thread to merge changes into the "main" context from worker threads - but I don't think that's the case.
I set up my importer to create batches of data which are imported using a subclass of an NSOperation that owns it's own context. The operations are loaded into an NSOperationQueue that's set to allow the default number of concurrent operations, so it's possible for several import batches to be running at the same time. I would occasionally get very strange validation errors and exceptions (like trying to add nil to a relationship) and other failures that I had never seen when I did all the same stuff on the main thread. It occurred to me (and perhaps this should have been obvious) that maybe the context merging needed to be done for all contexts in every thread - not just the "main" one! I don't know why I didn't think of that before, but I think this helped. (It hasn't been tested well enough yet for me to feel sure, though.) In any case, is it true that you need to observe that notification for ALL import threads that may be working with the same datasets and adding/updating the same entities? If so, this is yet another pitfall bullet point, IMO, although I have yet to be certain that it'll work.
Given how many of these I've run into with Core Data in general (and not all of them just about multi-threading), I have to wonder how many more are lurking. Since multi-threading so often ends up with bugs that are difficult if not impossible to reproduce due to the timing issues, I figured I'd ask if anyone had other important things that I may be missing that I need to concern myself with.
There is an entire rather large bit of documentation devoted to the subject of Core Data and Threading.
It isn't clear from your set of issues what isn't covered by that documentation.

In Cocoa do I need to remove an Object from receiving KVO notifications when deallocating it?

When I've registered an object foo to receive KVO notifications from another object bar (using addObserver:...), if I then deallocate foo do I need to send a removeObserver:forKeyPath: message to bar in -dealloc?
You need to use -removeObserver:forKeyPath: to remove the observer before -[NSObject dealloc] runs, so yes, doing it in the -dealloc method of your class would work.
Better than that though would be to have a deterministic point where whatever owns the object that's doing the observing could tell it it's done and will (eventually) be deallocated. That way, you can stop observing immediately when the thing doing the observing is no longer needed, regardless of when it's actually deallocated.
This is important to keep in mind because the lifetime of objects in Cocoa isn't as deterministic as some people seem to think it is. The various Mac OS X frameworks themselves will send your objects -retain and -autorelease, extending their lifetime beyond what you might otherwise think it would be.
Furthermore, when you make the transition to Objective-C garbage collection, you'll find that -finalize will run at very different times — and in very different contexts — than -dealloc did. For one thing, finalization takes place on a different thread, so you really can't safely send -removeObserver:forKeyPath: to another object in a -finalize method.
Stick to memory (and other scarce resource) management in -dealloc and -finalize, and use a separate -invalidate method to have an owner tell an object you're done with it at a deterministic point; do things like removing KVO observations there. The intent of your code will be clearer and you will have fewer subtle bugs to take care of.
A bit of extra info that I've gained by painful experience: although NSNotificationCenter uses zeroing weak references when running under garbage collection, KVO does not. Thus, you can get away with not removing an NSNotificationCenter observer when using GC (when using retain/release, you still need to remove your observer), but you must still remove your KVO observers, as Chris describes.
Definitely agree with Chris on the "Stick to memory (and other scarce resource) management in -dealloc and -finalize..." comment. A lot of times I'll see people try to invalidate NSTimer objects in their dealloc functions. The problem is, NSTimer retains it's targets. So, if the target of that NSTimer is self, dealloc will never get called resulting in some potentially nasty memory leaks.
Invalidate in -invalidate and do other memory cleanup in your dealloc and finalize.

Resources