Multithreading violation core data

那年仲夏 提交于 2019-12-11 08:49:29

问题


I have an app where I download the data on startup using a list of operations and it crashes randomly for unknown core data reasons so I spent few days on checking the best practices to update/fetch data in multithreading core data with MagicalRecord. One of the options was to enable the multithreading debugger -com.apple.CoreData.ConcurrencyDebug 1 where Xcode stops the apps when it violates one of their rules. So, Xcode stops my app on this line [SyncRequestEntity MR_createEntityInContext:[self getPrivateContext]]

+ (MagicalRecordVersionNumber) version
{
    return MagicalRecordVersionNumber2_3;
}
@implementation NSManagedObjectContext (MagicalRecord) 

+ (NSManagedObjectContext *) MR_context
{
    return [self MR_contextWithParent:[self MR_rootSavingContext]];
}

+ (NSManagedObjectContext *) MR_contextWithParent:(NSManagedObjectContext *)parentContext
{
    NSManagedObjectContext *context = [self MR_newPrivateQueueContext];
    [context setParentContext:parentContext];
    [context MR_obtainPermanentIDsBeforeSaving];
    return context;
}

- (void) MR_obtainPermanentIDsBeforeSaving
{
    [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(MR_contextWillSave:)
                                                     name:NSManagedObjectContextWillSaveNotification
                                                   object:self];
}
+ (NSManagedObjectContext *) MR_newPrivateQueueContext
{
    NSManagedObjectContext *context = [[self alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    MRLogInfo(@"Created new private queue context: %@", context);
    return context;
}

@end

@implementation MyClass

    - (NSManagedObjectContext *) getPrivateContext
    {
        if (self.privateContext == nil)
        {
            self.privateContext = [NSManagedObjectContext MR_context];
        }
        return self.privateContext;
    }

    - (SyncRequestEntity *) getSyncRequest
    {
        SyncRequestEntity *syncRequest = [SyncRequestEntity MR_findFirstByAttribute:@"key" withValue:self.itemKey inContext:[self getPrivateContext]];

        // Checking if the entity was sync previously with the same filters.
        if (syncRequest == nil)
        {
            syncRequest = [SyncRequestEntity MR_createEntityInContext:    [self getPrivateContext]];
        }

        return syncRequest;
    }
@end

@implementation NSManagedObject (MagicalRecord)
+ (id) MR_createEntityInContext:(NSManagedObjectContext *)context
{
    if ([self respondsToSelector:@selector(insertInManagedObjectContext:)] && context != nil)
    {
        id entity = [self performSelector:@selector(insertInManagedObjectContext:) withObject:context];
        return entity;
    }
    else
    {
        NSEntityDescription *entity = nil;
        if (context == nil)
        {
            entity = [self MR_entityDescription];
        }
        else
        {
            entity  = [self MR_entityDescriptionInContext:context];
        }

        if (entity == nil)
        {
            return nil;
        }

        return [[self alloc] initWithEntity:entity insertIntoManagedObjectContext:context];
    }
}
@end

The privateContext is a local variable for each operation so I have private contexts for each operation in order to not interrupt the main one. The point is that I create one private context for each thread and I'm just trying to create a new NSManagedObject instance using this context and Xcode says that I'm violating the multithreading core data rules. Does anyone have any clue on what's happening?


回答1:


We have the same issue when developing our own app.

When you try to perform a write operation in a thread different to the context one, it crash sometimes.

Our solution was to make a private manager on the AppDelegate.m file. Just add this code:

- (NSManagedObjectContext *)getPrivateManagedObjectContext
{
    if (self.managedObjectContext != nil) {
        return self.managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self getPersistentStoreCoordinator];
    if (coordinator != nil) {
        self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [self.managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return self.managedObjectContext;
}

Then when you need to perform any operation, you should use this method that ensures the block is running on the same thread of the context:

[self.managedObjectContext performBlock:^{...}];
[self.managedObjectContext performBlockAndWait:^{...}];


来源:https://stackoverflow.com/questions/32206967/multithreading-violation-core-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!