MagicalRecord:删除后保存上下文太长

Posted

技术标签:

【中文标题】MagicalRecord:删除后保存上下文太长【英文标题】:MagicalRecord: saving context after deleting is too long 【发布时间】:2015-01-10 22:58:44 【问题描述】:

我有下一个问题:我有大约 7000 个实体的数据库,当我需要更新它们(我有我解析的 XML 文件)时,我首先删除所有实体,之后我解析 XML 文件,然后我创建新的实体并保存上下文。早些时候一切都很完美:没有冻结,没有崩溃 - 在 ios 7 上一切都很快。

但是随着 iOS 8 的发布,出现了一些问题:

我通过为所有操作提供一个上下文来解决这个问题:删除、创建和保存。

但是!我有什么:

当我只是在我的设备上安装应用程序时一切顺利:没有删除,只创建实体,7000 个术语和 7 个组被解析得如此之快(在 iPhone 6 上大约 4 秒),保存也很快。

当我在我的 plist 文件中更改数据库版本(增加)时,我的解析器会启动这个算法:


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
        [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 
            [Term MR_truncateAllInContext:localContext];
            [Group MR_truncateAllInContext:localContext];
         completion:^(BOOL success, NSError *error) 
            [self parseTermsInContext:[NSManagedObjectContext contextForCurrentThread] from:self.count];
        ];
    );

“saveWithBlock”方法阻塞线程 1(在分析器中),我的 CPU 加载到 99-108(显然错误)百分比(每次下一次更新保存操作需要越来越多的秒数,从 20 秒到更多,超过 120 秒) .

我已经尝试过这种方式(我为你收集了一种方法中的所有操作):

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
        NSManagedObjectContext *localContext = [NSManagedObjectContext contextForCurrentThread];
        NSMutableArray *objects = [NSMutableArray arrayWithArray:[Group MR_findAllInContext:localContext]];
        [objects addObjectsFromArray:[Term MR_findAllInContext:localContext]];

        if (objects && [objects count] > 0) 
            for (NSManagedObject *object in objects) 
                [object MR_deleteInContext:localContext];
            
            [localContext MR_saveToPersistentStoreAndWait];
        
        [self parseTermsInContext:localContext from:self.count];
    );

这里的操作“MR_saveToPersistentStoreAndWait”也像“saveWithBlock”一样需要很长时间。

我尝试了删除后不保存上下文的方法,即“[localContext MR_saveToPersistentStoreAndWait];”行不存在。这样一来,Groups 和 Terms 也被删除得太快了,后来它们也被解析得太快了,但是保存上下文却太长了。

而且我不知道为什么,但即使我在后台线程保存操作中开始删除和保存进程,也会冻结 UI 线程(在 UI 线程中,我显示的进度从 0 到 100)。当我在这个线程中解析 XML 时,我发送消息以查看一个术语已解析并以百分比设置进度,委托调用方法以在主队列中的 ProgressView 中设置进度。

我没有其他线程可以操作核心数据对象。

与应用程序的工作有链接:http://rghost.ru/60274051 6 秒后:出于测试目的,我启动 NSTimer,它每 0.3 秒用假数据更新进度,以在开始删除和保存操作之前填充进度 50%(更新进度进入主队列)。计时器触发几次,然后保存过程在后台线程中开始,但阻塞主线程(据我所知)并将设置进度操作移动到结束(如果我理解正确的话)。 1:08:然后在保存结束后我开始解析 xml 文件。这是我在删除后保存上下文的线程。您可以看到进度更新。在这个视频中,由于有很多操作,它可以处理错误,但你可以相信我,它可以工作并且看起来很好。解析 7000 个对象后,我再次保存上下文,保存操作不会阻塞 UI 线程。

附加信息: 关系:

【问题讨论】:

【参考方案1】:
     completion:^(BOOL success, NSError *error) 
        [self parseTermsInContext:[NSManagedObjectContext contextForCurrentThread] from:self.count];
    ];

上面的块在主线程中调用(块 UI),所以 contextForCurrentThread == UI 线程

您可以使用 TruncateAll 代替迭代删除所有对象

我的建议是:

// saveWithBlock - already perform block in background thread
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 
    [Term MR_truncateAllInContext:localContext];
    [Group MR_truncateAllInContext:localContext];
 completion:^(BOOL success, NSError *error) 
    [MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) 
        // self must be weak!
        [self parseTermsInContext:localContext from:self.count];
     completion:^(BOOL success, NSError *error) 
        // Update UI
    ];
];

【讨论】:

以上是关于MagicalRecord:删除后保存上下文太长的主要内容,如果未能解决你的问题,请参考以下文章

使用 Swift 3 在 Magical Record 中保存上下文时出错

MagicalRecord 并不总是保存更改

MagicalRecord UIApplicationDidEnterBackground

MagicalRecord 不保存对多关系实体

撤消托管对象删除

保存和重新访问后对象字段仍然出错 (MagicalRecord)