Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing the objectID of a NSManagedObject on a different thread?

Part of my UITableViewCell's content creation is delayed by the fault that happens on one object's (CoreData NSManagedObject) initial access. This manifests itself in a small hiccup the cell is first scrolled into view. I decided to push that access of those objects off to a background thread.

This is how I implemented it and it works well, but we all know that we are not supposed to access one thread(the main thread)'s NSManagedObjectContext in another thread, but can we get the objectID of an object in a second thread if it was originally fetched in the first thread?

Getting the objectID takes a small amount of time, which I was hoping to push into the background with everything else.

MyRecord *record = [self.frc objectAtIndexPath: indexPath];

// Should the following be here or can it be below in the background thread?
// NSManagedObjectID *recordObjectID = record.objectID;

dispatch_async(_recordViewQueue, ^(void) {
    if ([cell.origIndexPath isEqual:indexPath]) {

        // should the following be here or above?  It works here, but am I just lucky?
        // this call seems to take about 2/100 of a second
        NSManagedObjectID *recordObjectID = record.objectID;

        NSManagedObjectContext *bgndContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
        bgndContext.persistentStoreCoordinator = App.sharedApp.storeCoordinator;
        MyRecord *newRecord = (MyRecord *) [bgndContext objectWithID:recordObjectID];

        [self updateCell:cell withRecord:newRecord];

        if ([cell.origIndexPath isEqual:indexPath]) {
            dispatch_async(dispatch_get_main_queue(), ^{
                [(UIView*) cell.recordView setNeedsDisplay];
            });
        }
    }
});

Is this safe? Or do I have to get the objectID in the mainThread?

like image 516
mahboudz Avatar asked Jan 22 '26 16:01

mahboudz


1 Answers

It is safe to pass the objectID of a managed object between threads. It is not safe to use a managed object between threads. Use the objectID and your thread's managed object context to call existingObjectWithID:error: to get an instance of the managed object for that thread.

I would update your code like so:

MyRecord *record = [self.frc objectAtIndexPath: indexPath];

NSManagedObjectID *recordObjectID = record.objectID;

dispatch_async(_recordViewQueue, ^(void) {
    if ([cell.origIndexPath isEqual:indexPath]) {

        NSManagedObjectContext *bgndContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSConfinementConcurrencyType];
        bgndContext.persistentStoreCoordinator = App.sharedApp.storeCoordinator;
        NSError * error = nil;
        MyRecord *newRecord = (MyRecord *) [bgndContext existingObjectWithID:recordObjectID error:&error];
        if (newRecord) {
            [self updateCell:cell withRecord:newRecord];
            if ([cell.origIndexPath isEqual:indexPath]) {
                dispatch_async(dispatch_get_main_queue(), ^{
                    [(UIView*) cell.recordView setNeedsDisplay];
                });
            }
        }
        else {
            NSLog(@"unable to find existing object! error: %@ (userInfo: %@)", [error localizedDescription], [error userInfo]);
        }
    }
});
like image 113
Nicholas Hart Avatar answered Jan 24 '26 09:01

Nicholas Hart



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!