【问题标题】:Coredata random crashes 1560, 1550Coredata随机崩溃1560、1550
【发布时间】:2017-07-25 09:12:01
【问题描述】:

有时它会给出这个

    2017-07-25 11:57:51.839 Test[14097:17556837] CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
2017-07-25 11:57:51.852 Test[14097:17556837] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack:
(
    0   CoreFoundation                      0x01573a94 __exceptionPreprocess + 180
    1   libobjc.A.dylib                     0x00c9be02 objc_exception_throw + 50
    2   CoreFoundation                      0x015739bd +[NSException raise:format:] + 141
    3   CoreFoundation                      0x01449d69 -[__NSCFSet addObject:] + 185
    4   CoreData                            0x010c8e00 -[NSManagedObjectContext(_NSInternalChangeProcessing) _processPendingInsertions:withDeletions:withUpdates:] + 560
    5   CoreData                            0x010c3a1c -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:] + 2204
    6   CoreData                            0x010c3166 -[NSManagedObjectContext processPendingChanges] + 54
    7   CoreData                            0x01096355 _performRunLoopAction + 357
    8   CoreFoundation                      0x0148d77e __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 30
    9   CoreFoundation                      0x0148d6de __CFRunLoopDoObservers + 398
    10  CoreFoundation                      0x0148305c __CFRunLoopRun + 1340
    11  CoreFoundation                      0x01482866 CFRunLoopRunSpecific + 470
    12  CoreFoundation                      0x0148267b CFRunLoopRunInMode + 123
    13  GraphicsServices                    0x0801e664 GSEventRunModal + 192
    14  GraphicsServices                    0x0801e4a1 GSEventRun + 104
    15  UIKit                               0x01e5dcc1 UIApplicationMain + 160
    16  Test                                0x000f3a2b main + 75
    17  libdyld.dylib                       0x0496fa21 start + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

有时它会给我这个

fatal error: Failure to save context: Error Domain=NSCocoaErrorDomain Code=1550 “The operation couldn’t be completed. (Cocoa error 1550.)” UserInfo={NSLocalizedDescription=The operation couldn’t be completed. (Cocoa error 1550.), Dangling reference to an invalid object.=null, NSValidationErrorObject=<Test.Article: 0x7c191030> (entity: Article; id: 0x7c1927d0 <x-coredata:///Article/t8BA04531-EB12-479D-9C0E-FF22ADE34A62201> ; data: {
    category = “0x7aeecc30 <x-coredata://5095458E-7D52-4717-A948-E58E1C13176D/Category/p27>“;
    categoryID = 5;
    content =     (
        “0x7c457250 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62202>“,
        “0x7c18b890 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62203>“,
        “0x7c454840 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62204>”
    );
    featuredImage = “600x600(19).jpg”;
    id = 1;
    issueID = 1;
    mainImage = “1.jpg”;
    state = downloaded;
    title = “New Title”;
    version = “1.0”;
}), NSAffectedObjectsErrorKey=(
    “<Test.Content: 0x7c454880> (entity: Content; id: 0x7c454840 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62204> ; data: {\n    article = nil;\n    content = \“<p>New Content For Testing</p>\“;\n    imageID = nil;\n    ordering = 3;\n    typeID = Paragraphs;\n})”
), NSValidationErrorKey=content, NSValidationErrorValue=Relationship ‘content’ on managed object (0x7c191030) <Test.Article: 0x7c191030> (entity: Article; id: 0x7c1927d0 <x-coredata:///Article/t8BA04531-EB12-479D-9C0E-FF22ADE34A62201> ; data: {
    category = “0x7aeecc30 <x-coredata://5095458E-7D52-4717-A948-E58E1C13176D/Category/p27>“;
    categoryID = 5;
    content =     (
        “0x7c457250 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62202>“,
        “0x7c18b890 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62203>“,
        “0x7c454840 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62204>”
    );
    featuredImage = “600x600(19).jpg”;
    id = 1;
    issueID = 1;
    mainImage = “1.jpg”;
    state = downloaded;
    title = “New Title”;
    version = “1.0”;
}) with objects {(
    <Test.Content: 0x7c459270> (entity: Content; id: 0x7c457250 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62202> ; data: {
    article = nil;
    content = “<p>Another Content with &lt;p&gt; Tag</p>“;
    imageID = nil;
    ordering = 1;
    typeID = Introduction;
}),
    <Test.Content: 0x7c190cf0> (entity: Content; id: 0x7c18b890 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62203> ; data: {
    article = nil;
    content = “Last Content”;
    imageID = nil;
    ordering = 2;
    typeID = Illustration;
}),
    <Test.Content: 0x7c454880> (entity: Content; id: 0x7c454840 <x-coredata:///Content/t8BA04531-EB12-479D-9C0E-FF22ADE34A62204> ; data: {
    article = nil;
    content = “<p>New Content For Testing</p>“;
    imageID = nil;
    ordering = 3;
    typeID = Paragraphs;
})
)}, NSValidationErrorShouldAttemptRecoveryKey=true}: file /Users/user/Documents/Development/Test/Test/Issues/IssuesViewController.swift, line 322

我相信这是由于并发性,但也不确定关系 以下是文章和内容模型

这是主要的 API。我是否正确使用了执行块?我应该使用它吗?我是否过度使用它?

func getArticleDetailsForArticleId(whereArticleId articleId: String, andCategoryObj categoryObj: Category)
    {
        //let issue = (Array(categoryObj.issue!) as! [Issue])[0]
        let group = /*issue.articleDispatchGroup*/categoryObj.issue!.articleDispatchGroup
        let queue = /*issue.articleQueue*/categoryObj.issue!.articleQueue
        var errors = /*issue.articleErrors*/categoryObj.issue!.articleErrors

        group.enter()
        let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
        privateMOC.parent =  (UIApplication.shared.delegate as! AppDelegate).managedObjectContext

        group.enter()
        privateMOC.perform {

        categoryObj.state = State.downloading.rawValue
        do {
            try privateMOC.save()
            privateMOC.parent!.perform {
                do {
                    try privateMOC.parent!.save()
                } catch {
                    fatalError("Failure to save context: \(error)")
                }
            }
        } catch {
            fatalError("Failure to save context: \(error)")
        }
        }

        print("saved category is downloading")
        DataManager.sharedInstance.getArticleDetails(whereArticleId: articleId, andCompletionHandler:  { (success, response) in

            let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
            privateMOC.parent =  (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
            print("I am articleId \(articleId)")
            // SVProgressHUD.dismiss()
            //self.apiInProgress = false
            if success
            {
                privateMOC.perform/* queue.async*/{

                let articleDetail = response.responseData as! Article
                articleDetail.state = State.downloaded.rawValue

                //queue.async {

                //categoryObj.addToArticles(articleDetail)
                //categoryObj.


                     //categoryObj.addToArticles(articleDetail)
                    let articles = NSMutableOrderedSet(orderedSet: categoryObj.articles!)
                    for content in Array(articleDetail.content!)
                    {
                        (content as! Content).article = articleDetail // should I add this line or the inverse relatioship is enough to set this
                        //privateMOC.parent?.insert(content as! Content)
                    }
                   // privateMOC.parent?.insert(articleDetail)

                    //articleDetail.content = NSOrderedSet(array: articleDetail.contents!)
                    articles.add(articleDetail)
                     categoryObj.articles = articles

                    //articleDetail.category = categoryObj
                    //categoryObj.theArticles.append(articleDetail)

                    categoryObj.issue!.articlesDownloaded += 1//categoryObj.issue?.articlesDownloaded += 1
                    let progress = CGFloat(categoryObj.issue!.articlesDownloaded)/CGFloat(categoryObj.issue!.articlesCount)
                    OperationQueue.main.addOperation({
                        self.issuesToProgressDictionary[categoryObj.issue!]?.pathFromProgress(whereProgress: progress,andFillColor: UIColor(red: 64/1255.0, green: 121/255.0, blue: 117/255.0, alpha: 0.4),andStrokeColor: UIColor.clear)
                        categoryObj.state = State.downloaded.rawValue

                        //(UIApplication.shared.delegate as! AppDelegate).saveContext()
                    })
                    do {
                        try privateMOC.save()
                        privateMOC.parent!.perform {
                            do {
                                try privateMOC.parent!.save()
                            } catch {
                                let categoryObjec = categoryObj
                                let articDet = articleDetail
                                fatalError("Failure to save context: \(error)")
                            }
                        }
                    } catch {
                        fatalError("Failure to save context: \(error)")
                    }  
                } 
            }  
    else
    {
    queue.async {
    errors.append(response.responseError!.errorMessage!)
    }
    let alert = UIAlertController(title: "OOPS", message: response.responseError?.errorMessage, preferredStyle: .alert)
    alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
    alert.addAction(UIAlertAction(title: "Try Again", style: .default, handler: { (action) in
    self.getIssues()
    }))
    self.present(alert, animated: true, completion: nil)
    }
    group.leave()
})

}

这是一个示例 NSManagedObject:文章

//
//  Article+CoreDataClass.swift
//
//
//  Created by User on 7/12/17.
//
//

import Foundation
import CoreData
import ObjectMapper

//@objc(Article)
public class Article: NSManagedObject, Mappable {

    var contents : [Content]?
    //var content: NSOrderedSet?
    required public init?(map: Map) {
        let context = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
        let entity = NSEntityDescription.entity(forEntityName: "Article", in: context)

        super.init(entity: entity!, insertInto: context)
        self.mapping(map: map)
        self.content = NSOrderedSet(array: self.contents!)
          }
        //        }

    }

    override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
        super.init(entity: entity, insertInto: (UIApplication.shared.delegate as! AppDelegate).managedObjectContext)
    }


    //private override init(){}

    public func mapping(map: Map)
    {

        featuredImage <- map["FeaturedImage"]
        issueID <- map["IssueID"]
        mainImage <- map["MainImage"]
        title <- map["Title"]
        version <- map["Version"]
        categoryID <- map["categoryID"]
        id <- map["ArticleID"]
        contents <- map["Content"]
        //content =  NSOrderedSet(array: map["Content"] as! [Content])
        state = State.nothing.rawValue
    }

}

【问题讨论】:

    标签: ios core-data objectmapper


    【解决方案1】:

    看线:

      DataManager.sharedInstance.getArticleDetails(whereArticleId: articleId, andCompletionHandler:  { (success, response) in
    

    在这里,您从一个线程请求 CoreData 模型并将它们处理到另一个线程:

    privateMOC.perform/* queue.async*/{
    
     let articleDetail = response.responseData as! Article
     articleDetail.state = State.downloaded.rawValue
     <....>
    

    您不应在 threads 之间共享 CoreData 模型。

    NSManagedObject 实例不打算在 排队。这样做可能会导致数据损坏和终止 的应用程序。需要移交托管对象时 从一个队列到另一个队列的引用,必须通过 NSManagedObjectID 实例。

    【讨论】:

    • 我尝试添加这个 let categoryObj = privateMOC.parent!.object(with: categoryObj.objectID) as!类别 - 就在 DataManager.sharedInstance.getArticleDetails {} //没有区别
    • 您确定来自当前线程的“categoryObj.objectID”吗?我认为 getArticleDetails 方法的 complitionHandler 是在主线程上调用的,但 request 是在后台线程上执行的。如果不是这样,可能是我错了。
    • 我认为你应该从 getArticleDetails 方法返回一个 UID 数组(而不是对象数组)到 complitionHandler。
    • getArticleDetails 是一个 API 调用,因此在 completionHandler 中返回成功 {true or false} 和响应 {json mapped into article object}
    【解决方案2】:

    正如已经解释过的,主要问题是您将对象从一个上下文传递到另一个上下文。您似乎有许多方法假设要使用什么上下文,而不是将上下文作为参数传递。

    例如:

     override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
        super.init(entity: entity, insertInto: (UIApplication.shared.delegate as! AppDelegate).managedObjectContext)
    }
    

    您假设所有对象创建都将在主上下文中进行。任何创建具有不同上下文的对象的尝试都会崩溃。您似乎正试图通过始终获取主上下文来使对象创建更简单、更方便。这是一个错误。由于不容易知道您使用的是什么上下文,因此您的代码 FAR 变得更加复杂。

    同样,DataManager.sharedInstance.getArticleDetails 方法似乎会执行获取并在块中返回结果。它使用哪个上下文?它返回哪个线程?如果它使用主上下文,那么它必须在主线程上运行还是在内部执行 dispatch_async?很难用你拥有的代码知道,因为提取的上下文是隐藏的。如果您无法跟踪您使用的上下文,那么在任何地方添加 performBlock 都不会解决您的问题。相反,getArticleDetails(whereArticleId:, inContext:) 的方法更容易理解和使用。

    我不明白你的大部分代码。这似乎是一个大混乱。我看起来你开始崩溃了,所以你添加了更多的 performBlock 和更多的线程,而没有解决任何真正的问题。要理解的关键点是,对于每个核心数据操作——获取、插入、更新——你必须知道上下文是什么,并且必须从正确的线程中调用它。因此,如果您有一个获取核心数据的方法,您必须向它传递一个上下文。如果你只是假设上下文是主要上下文,你会搞砸的。

    如果你有一个方法被传递了一个 managedObject 那么它应该只使用它被传递和返回的对象的上下文。传递 managedObjects 然后使用不同的上下文是灾难的根源。

    我建议使用NSPersistentContainer。使用viewContext 进行所有阅读,使用performBackgroundTask: 进行所有写作。不要在块外使用在performBackgroundTask: 创建的任何对象。

    【讨论】:

    • 我尝试在主队列上运行东西,因为我使用的是主上下文。有时它还是会崩溃。
    • 我希望我能提供更多帮助,但我真的无法理解您的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-17
    • 2013-08-23
    • 2013-04-21
    • 2011-08-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多