执行块中的Coredata多线程违规

时间:2017-05-26 19:10:19

标签: objective-c swift multithreading core-data magicalrecord

对于我的生活,我无法弄清楚出了什么问题。我试图找出为什么coredata说我有一个并发问题。在我的tableviewCellForRow方法中,我试图在用户完全向下滚动时加载更多数据。我想在后台上下文中执行此操作,然后保存结果。后台上下文是我的主要上下文的子项(它具有并发类型private)。我正在做:

            let backgroundContext = NSManagedObjectContext.mr_newPrivateQueue()
            backgroundContext.parent = NSManagedObjectContext.mr_default()
            backgroundContext.perform({ 
                self.loadMoreConversationsThenSave(inContext: backgroundContext)

                // We want to run any UI updates on the main thread or we have weird crashes/undefined results
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            })

然后在loadMoreConversations方法中如下所示:

fileprivate func loadMoreConversationsThenSave(inContext context: NSManagedObjectContext) {
    let oldestConvoIndex = (conversationsFRC.fetchedObjects?.count ?? 0 ) - 1

    if oldestConvoIndex > 0 {
        let oldestConvo = conversationsFRC.fetchedObjects?[oldestConvoIndex] as? Conversation
        let oldestConvoInContext = context.object(with: (oldestConvo?.objectID)!) as? Conversation
        let oldestCallId = oldestConvoInContext?.most_recent_call_id
        let oldestCall = FetchRequestHelper.fetch(class: Call.self, withID: oldestCallId, inContext: context) as? Call
        if let date = oldestCall?.created_at {
            BPDKAPIClient.shared().getConversationsSince(date, isAfterDate: false, completion: { (objects, error) in
                if error != nil {
                    print("ERROR - failed to fetch conversations since date \(error)")
                } else {
                    if objects?.count == 0 {
                        self.recevedAllCallsFromServer = true
                    } else {

                        // Since we're in the callback this is going to be run on the main thread, so we need to wrap
                        // it in a perform block
                        context.perform({
                            Conversation.parseConversationAndCalls(objects, in: context) //points here
                            do {
                                try context.save()
                            } catch {
                                print("ConversationsTVC: Error saving: \(error)")
                            }
                            // We want to run any UI updates on the main thread or we have weird crashes/undefined results
                            DispatchQueue.main.async {
                                self.tableView.reloadData()
                            }
                        })
                    }
                }
            })
        }
    }
}

获取请求帮助程序类创建一个获取请求和一个FRC,然后只使用该ID获取对象并返回它。所有这些都是通过传入的相同上下文完成的。请注意,当API调用getConversationsSince完成时,我们希望在相同的上下文中执行另一个perform因为完成该函数是在主线程而不是相同的后台线程上启动的。

断点指向旁边带有注释的行:Conversation.parseConversationAndCalls(objects, in: context)

这就是该方法的样子:

+ (void) parseConversationAndCalls:(NSArray*)objects inContext:(NSManagedObjectContext*)localContext {

    for (NSArray *conversationBundle in objects) {

        NSArray *conversationsCallData = conversationBundle[1];
        NSArray *conversationData = conversationBundle[0];
        NSNumber *convoID = [BPDObject scrubbedInteger:conversationData[0]];
        Conversation *convo = (Conversation*) [FetchRequestHelper fetchWithClass:Conversation.self withID:convoID inContext:localContext]; //points here

        if (convo == nil) {
            convo = [Conversation insertInManagedObjectContext:localContext];
        }

        [convo updateWithValues:conversationData];
        Contact *contact = (Contact*) [FetchRequestHelper fetchWithClass:Contact.self withID:convo.contact_id inContext:localContext];

        // getCOnversations since date will supply all call data as well
        for (NSArray *callData in conversationsCallData) {

            NSNumber *callID = [BPDObject scrubbedInteger:callData[1]];
            Call *call = (Call*) [FetchRequestHelper fetchWithClass:Call.self withID:callID inContext:localContext];

            if (call.objectID.isTemporaryID) {
                call = [Call insertInManagedObjectContext:localContext];
            }

            [call updateWithValues:callData inContext:localContext];
            call.contact = contact;
        }
    }
}

此方法中指向的行是:Conversation *convo = (Conversation*) [FetchRequestHelper fetchWithClass:Conversation.self withID:convoID inContext:localContext];或任何其他FetchRequestHelper方法。我也不认为它与获取请求助手有关(我已经查看了50次)但是如果你有点好奇的话就是这样:

class FetchRequestHelper: NSObject {

    public class func fetchRequest(theClass: BPDObject.Type, predicate: NSPredicate, sortDescriptors: NSArray, inContext context: NSManagedObjectContext) -> NSFetchRequest<NSFetchRequestResult> {
        let request = theClass.mr_requestAllSorted(by: "", ascending: true, with: predicate, in: context)
        request.sortDescriptors = sortDescriptors as? [NSSortDescriptor]
        request.includesPropertyValues = false
        request.fetchBatchSize = 20
        request.includesPendingChanges = false

        return request
    }

    public class func fetchedResultsController(forRequest request: NSFetchRequest<NSFetchRequestResult>, context: NSManagedObjectContext) -> NSFetchedResultsController<NSFetchRequestResult> {
        let fetchedResultController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)

        return fetchedResultController
    }

    public class func fetch(class someClass: BPDObject.Type, withID id: NSNumber!, inContext context: NSManagedObjectContext) -> BPDObject? {
        let predicate = NSPredicate(format: "id == %@", id)
        let request = FetchRequestHelper.fetchRequest(theClass: someClass, predicate: predicate, sortDescriptors: [], inContext: context)
        let frc = FetchRequestHelper.fetchedResultsController(forRequest: request, context: context)

        do {
            try frc.performFetch()
        } catch {
            fatalError("Failed to perform fetch from FetchedResultsController: \(error)")
        }

        return frc.fetchedObjects?.first as? BPDObject
    }
}

我怀疑它可能与API调用完成块有关,但我已经在performBlock中包装了下一个操作。可能是什么问题?

0 个答案:

没有答案