多线程违规核心数据

Posted

技术标签:

【中文标题】多线程违规核心数据【英文标题】:Multithreading violation core data 【发布时间】:2015-08-25 14:37:18 【问题描述】:

我有一个应用程序,我在启动时使用操作列表下载数据,但由于未知的核心数据原因,它随机崩溃,所以我花了几天时间检查使用 MagicalRecord 在多线程核心数据中更新/获取数据的最佳实践。其中一个选项是启用多线程调试器-com.apple.CoreData.ConcurrencyDebug 1,当 Xcode 违反其中一项规则时,它会停止应用程序。所以,Xcode 在这条线上停止了我的应用程序[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

privateContext 是每个操作的局部变量,因此我对每个操作都有私有上下文,以免中断主要操作。关键是我为每个线程创建了一个私有上下文,我只是试图使用这个上下文创建一个新的NSManagedObject 实例,Xcode 说我违反了多线程核心数据规则。有没有人知道发生了什么?

【问题讨论】:

MR_context的代码是什么?您是否考虑到块可以在不同的线程上运行? 添加细节@AminNegm-Awad。我这里没有使用任何块。 您使用的是哪个版本的 MagicalRecord? @casademora,你可以在上面更新的代码中看到它是 2.3。 【参考方案1】:

我们在开发自己的应用程序时遇到了同样的问题。

当您尝试在与上下文不同的线程中执行写入操作时,它有时会崩溃。

我们的解决方案是为AppDelegate.m 文件设置一个私人管理员。只需添加此代码:

- (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;

那么当你需要执行任何操作时,你应该使用这种方法来确保块在上下文的同一个线程上运行:

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

【讨论】:

为什么需要使用performBlock?我已经在后台线程(NSOperation)上我只想使用我为每个操作创建的 privateContext 创建/获取/插入实体,但似乎我违反了他们的规则之一,到目前为止我还不明白。 问题是如果你同时执行写操作它可能会崩溃。您只需要使用一个私有上下文并在同一个上下文线程中执行每个操作。 拥有一个全局私有上下文和每个线程都有私有上下文有什么区别?在我看来,我会得到相同的结果,对吧? 主要问题是所有操作都应该在performBlock或performBlockAndWait中。只有一个,将确保所有线程都使用相同的“线程队列”。 我尝试为所有线程设置一个私有上下文,但得到了相同的结果..

以上是关于多线程违规核心数据的主要内容,如果未能解决你的问题,请参考以下文章

核心数据多线程获取记录

iPhone 核心数据和多线程

核心数据多线程[关闭]

核心数据多线程:代码示例

核心数据多线程问题

多线程核心数据 - NSManagedObject 无效