2017-07-25 60 views
0

有時它給這個Coredata隨機崩潰1560年,1550

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 

我相信這是由於併發性,而且是不確定的關係 以下是文章和內容模型

enter image description here enter image description here

這裏是主要的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 
    } 

} 

回答

0

看行:

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實例完成。

+0

我試着添加這個 讓categoryObj = privateMOC.parent!.object(with:categoryObj.objectID)as!類別 - 只是在DataManager.sharedInstance.getArticleDetails {} //裏面沒有區別 – thirdage

+0

你確定當前線程中的「categoryObj.objectID」?我認爲方法getArticleDetails的complitionHandler在主線程上被調用,但請求在後臺線程上執行。如果不是這樣,可能是我錯了。 –

+0

我想你應該從getArticleDetails方法返回complitionHandler UID數組(不是對象數組)。 –

0

如前所述,主要問題是您將對象從一個上下文傳遞到另一個上下文。您似乎有許多方法會假定要使用的上下文而不是作爲參數傳遞的上下文。

例如:

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

你是假設所有對象創建將發生在主背景。任何嘗試創建具有不同上下文的對象都會崩潰。看起來您正在嘗試通過始終獲取主要上下文來使對象創建更簡單,更方便。這是一個錯誤。由於您不容易知道您使用的是什麼環境,因此您將使代碼更加複雜。

同樣,方法DataManager.sharedInstance.getArticleDetails似乎做了一個提取並將結果返回到一個塊中。它使用哪種上下文?它返回什麼線程?如果它使用主要上下文,那麼它是否必須在主線程上運行,或者它是否在內部執行dispatch_async?由於取回的上下文隱藏起來,因此很難了解您的代碼。如果無法追蹤使用的上下文,則在任何地方添加performBlock都無法解決您的問題。取而代之的是,getArticleDetails(whereArticleId:, inContext:)的方法更易於理解和使用。

我不明白你的大部分代碼。這似乎是一個大混亂。我看起來像你開始崩潰,所以你添加更多的performBlock和更多的線程,而不修復任何真正的問題。要理解的關鍵是,對於每個核心數據操作 - 讀取,插入,更新 - 您必須知道上下文是什麼,並且必須從正確的線程調用它。所以如果你有一個方法在覈心數據中進行讀取,你必須將它傳遞給一個上下文。如果你只是假設上下文是主要的上下文,你會搞砸了。

如果您有一個傳遞managedObject的方法,那麼它應該只使用它傳遞和返回的對象的上下文。傳遞managedObjects然後使用不同的上下文是災難的祕訣。

我建議使用NSPersistentContainer。用viewContext做所有的閱讀,並用performBackgroundTask:來完成你所寫的全部內容。請勿使用塊外部performBackgroundTask:中創建的任何對象。

+0

我嘗試在主隊列上運行的東西,因爲我使用的主要上下文。它仍然有時會崩潰。 – thirdage

+0

我希望我能幫到更多,但我真的不明白你的代碼。 –