CoreData 并发

Posted

技术标签:

【中文标题】CoreData 并发【英文标题】:CoreData concurrency 【发布时间】:2014-06-11 19:38:13 【问题描述】:

我遇到了 CoreData 持久性问题(MagicalRecord 2.2、ios 7.x) 可以在任何线程上创建和使用主对象:

- (Collection *)collection 

    if (!_collection) 
        [MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) 
            _collection = [Collection MR_createInContext:localContext];
            _collection.creationDate = [NSDate date];
            _collection.collectionDescription = @"";
            _collection.name = [CollectionHelper getNameForUnnamedCollection];
        ];
    

    return _collection;

在任何代码位置,我都可以开始在后台线程中向此集合添加资源:

    if (self.photoSavingBlocksCount == 0) 
        [self beginPhotoSaving];
    
    self.photoSavingBlocksCount++;
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 
    Collection *localCollection = [self.collection MR_inContext:localContext];
    [localCollection addNewResourceForImage:image thumb:thumb gallery:gallery type:RESOURCE_TYPE_IMAGE];
 completion:^(BOOL success, NSError *error) 
    self.photoSavingBlocksCount--;
    if (self.photoSavingBlocksCount == 0) 
        [self endPhotoSaving];
    
];

此代码将集合及其资源标记为已上传到服务器:

- (BOOL)markCollectionAsUploaded:(Collection *)collection 
    [MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) 
        Collection *localCollection = [collection MR_inContext:localContext];
        localCollection.uploaded = @YES;
        localCollection.uploadDate = [NSDate date];
    ];
    return YES;

此代码工作正常,但如果我在此之前编辑集合,则此代码将失败并且集合不会保存为上传! 换句话说,在下一次获取时,我将得到 collection.uploaded == @NO!

此代码保存编辑的集合

    [MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) 

            Collection *localCollection = [self.collection MR_inContext:localContext];

            NSSet *set = [NSSet setWithArray:self.deletedResources];

            localCollection.uploaded = @NO;
            localCollection.name = ([fieldName.text length] == 0 || [[fieldName.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0)
            ? [CollectionHelper getNameForUnnamedCollection]
            : fieldName.text;
            localCollection.collectionDescription = [fieldDescription.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];

            for (Resource *resource in set) 
                Resource *localResource = [resource MR_inContext:localContext];
                [CollectionHelper removeResourceFiles:resource];
                [localResource MR_deleteEntity];
            
        ];

在日志中我看到每个上下文保存的相同图片:

-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x196e2ee0) → Saving <NSManagedObjectContext (0x196e2ee0): *** UNNAMED ***> on *** BACKGROUND THREAD ***
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x196e2ee0) → Save Parents? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x196e2ee0) → Save Synchronously? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x17d83e30) → Saving <NSManagedObjectContext (0x17d83e30): *** BACKGROUND SAVING (ROOT) ***> on *** BACKGROUND THREAD ***
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x17d83e30) → Save Parents? 1
-[NSManagedObjectContext(MagicalSaves) MR_saveWithOptions:completion:](0x17d83e30) → Save Synchronously? 1

有人可以告诉我一个适合我的情况来管理 CoreData 对象的方法吗?我将不胜感激。

【问题讨论】:

【参考方案1】:

在您的第一个代码块中,您希望这样做:

- (Collection *)collection 

    if (!_collection) 
        [MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) 
            _collection = [Collection MR_createInContext:localContext];
            _collection.creationDate = [NSDate date];
            _collection.collectionDescription = @"";
            _collection.name = [CollectionHelper getNameForUnnamedCollection];
        ];
    
    _collection = [_collection MR_inContext:self.context];
    return _collection;

当您从保存块返回时,localContext 从内存中被丢弃,您的对象将无法保存。为避免此问题,您应该将这个新对象刷新到本地、寿命更长的上下文中。您可以完全避免这种模式,方法是不使用块并直接保存到更长寿的上下文中。

而且,老实说,我没有阅读问题的其余部分,因为应该首先澄清这一点,以避免出现晦涩的崩溃......

【讨论】:

感谢您的回答。我不明白你在 self.context 中提到了哪个上下文? @NeverBe 如果您想在应用程序代码中的任何位置从 - (Collection *)collection 方法获取数据,则需要持久化上下文。这就是为什么您需要将上下文存储在 self.context 属性中。

以上是关于CoreData 并发的主要内容,如果未能解决你的问题,请参考以下文章

CoreData 并发和释放对象

CoreData 和并发:无法解释的行为

CoreData 并发 - 我需要一个单独的 NSOperationQueue 吗?

CoreData 和一对多关系的并发错误

Coredata 并发问题

NSOperation 和 CoreData 线程