Search code examples
iosmultithreadingcore-datansmanagedobjectcontextui-thread

With a Private Queue NSManagedObjectContext, do I need to access properties of NSManagedObjects on the private queue?


I'm finally getting to my To-Do list item to update old app code to use a Private Queue for my NSManagedObjectContext, but I'm running into some awkward situations and I can't find what I consider a clear answer.

If I'm accessing simple properties on an NSManagedObject, do I need to do that on the Private Queue as well?

It's causing me problems with table views and such. For example:

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSManagedObject *object = [self.fetchedResultsController objectAtIndexPath:indexPath];

    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];
    
    cell.textLabel.text = [object valueForKey:@"name"];
}

Is that ok to do when my context is in a private queue and I'm accessing the object value from the main ui thread?

The above is a very simple example, just to explain my point, but I've got more complicated examples throughout my code. Like when dealing with building swipe actions, I feel like I'm bouncing back and forth between ui-private-ui-private-ui in order to just handle a tap on a button.


Solution

  • Managed objects can only be used on the queue where they were fetched, so if you fetch on a private queue, you can only use the object on that queue. That includes accessing the properties of the object. There are a few exceptions to this rule-- you can look up the managed object ID, for example, and do a few other basic things (see the class docs for a full list), but that doesn't directly help here.

    Depending on the details of your app, one or more of the following might help:

    • Use performBlockAndWait: to retrieve the property values. This might block the UI if the private queue is busy with something else though.
    • Change the fetch request to get dictionaries as the fetch result instead of managed objects, by setting resultType on the fetch request to NSDictionaryResultType. This is fine if you're only reading the data, but it may be awkward to save changes since you don't have a managed object to work on.
    • Get the object's ID and then use objectWithId: to look up the same object on a different context that uses the main queue. Or if you have a bunch of objects, you can do a new fetch for all of the object IDs, again on the main queue.
    • Fetch from a main-queue context.