2015-10-21 80 views
4

我的應用程序最近已經從crashlytics讓這些崩潰的是僅在iOS9發生崩潰的iOS9用 - [NSPersistentStoreCoordinator _coordinator_you_never_successfully_opened_the_database_device_locked:]

致命異常:NSInternalInconsistencyException
這NSPersistentStoreCoordinator沒有持久性存儲(文件損壞)。它不能執行保存操作。

從報告中的最後一次通話是

-[NSPersistentStoreCoordinator _coordinator_you_never_successfully_opened_the_database_device_locked:] 

,這是NSPersistentStoreCoordinator是如何創建的

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{ 
    if (_persistentStoreCoordinator != nil) { 
     return _persistentStoreCoordinator; 
    } 

    AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; 

    NSURL *storeURL = [[delegate applicationDocumentsDirectory] URLByAppendingPathComponent:@"database.sqlite"]; 

    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.managedObjectModel]; 

    NSError* error = nil; 

    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType 
                configuration:nil 
                  URL:storeURL 
                 options:@{NSMigratePersistentStoresAutomaticallyOption:@YES, NSInferMappingModelAutomaticallyOption:@YES} error:&error]) 
    { 
     NSLog(@"Error adding persistent store. %@, %@", error, error.userInfo); 
     return nil; 
    } 

    return _persistentStoreCoordinator; 
} 

有人知道這可能是造成這些事故?

回答

3

我在iOS9上沒有遇到過這個錯誤。不過,你應該看看你的日誌,看看你有什麼錯誤。是否有可能在創建PSC時遇到了「添加持久存儲時出錯」?

您的方法有一個問題,如果您遇到該錯誤,後續調用將返回既非零也未正確設置的PSC。

原因在於您在成功設置前指定_persistentStoreCoordinator。因此,如果有任何錯誤,您將返回零,但下一次您調用該方法時,您將返回一個沒有商店的PSC。

在任何情況下,您都應該更改該方法,以便您只能返回零或完全正常運行的PSC。

我會改變這種方法是這樣的。 注意,但是,我永遠不會像這樣構建核心數據棧。但是,至少下面的代碼將修復您的錯誤,您可以返回部分構成的PSC。

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator{ 
    if (_persistentStoreCoordinator != nil) { 
     return _persistentStoreCoordinator; 
    } 

    AppDelegate *delegate = (AppDelegate *)[UIApplication sharedApplication].delegate; 
    NSURL *storeURL = [[delegate applicationDocumentsDirectory] 
     URLByAppendingPathComponent:@"database.sqlite"]; 

    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] 
     initWithManagedObjectModel:self.managedObjectModel]; 
    NSError *error = nil; 
    if (![psc addPersistentStoreWithType:NSSQLiteStoreType 
          configuration:nil 
            URL:storeURL 
           options:@{NSMigratePersistentStoresAutomaticallyOption:@YES, 
              NSInferMappingModelAutomaticallyOption:@YES} 
            error:&error]) { 
     NSLog(@"Error adding persistent store. %@, %@", error, error.userInfo); 
    } else { 
     _persistentStoreCoordinator = psc; 
    } 

    return _persistentStoreCoordinator; 
} 

編輯

沒有足夠的空間在評論中回答你的問題,所以我把它放在這裏。

@JodyHagins - 另外,你提到你不會構建你的 這樣的堆棧,你能讓我知道我的堆棧有什麼問題嗎? - AWillian

我發表該評論是因爲您發佈的代碼與默認的Xcode核心數據模板極其相似。您調用應用程序委託來獲取目錄,表明這不在應用程序委託中,這很好。

然而,該方法表明你從任何地方懶惰地訪問它,這向我表明你的堆棧沒有以我將要構建堆棧的方式構建(特別是因爲你還包含了用於遷移的選項)。

我不是故意暗示你在做什麼是錯的,本身就是這樣,這不是我該怎麼做。現在

,我會是第一個說,我做的是我做什麼......我不說這是正確的方式...只是我的方式。實際上,我還沒有看到其他人真的在做我所做的事情(我實際上是NSManagedObjectContext的子類,但是我介意警告並遠離這些持久位)。這本身可能表明我所做的事可能並不適合你或任何其他人。但是,我發現它適合我,還有我必須實施的非常複雜的應用程序。

那麼,我將如何建立一個堆棧?

嗯,這是值得的東西深入比更加如此回答,所以我會盡量簡短。它還取決於哪種類型的堆疊 - 父/子,具有相同PSC的兄弟姐妹,具有不同PSC但相同商店的兄弟姐妹。

首先,我不提供單獨訪問模型和協調器。你可以很容易地從上下文訪問那些,並且通常最終導致比它的價值更多的問題。

的測試和簡單的例子之外,我始終異步創建我的MOC,這樣的事情...

+ (void)createWithConcurrencyType:(NSManagedObjectContextConcurrencyType)concurrencyType 
         completion:(void(^)(NSManagedObjectContext *moc, NSError *error))completion; 

,創建MOM和分配給PSC,創建PSC和分配給MOC 。它是異步發生的,因此可以在後臺線程中完成打開和初始化的潛在漫長過程。完成處理程序在performBlock之內調用,因此MOC可以乾淨地使用。這也可以防止在MOC完全安裝並讀取之前使用。

即使調用主隊列併發類型,所有的工作都在後臺線程中完成的,因此,只有在完成被稱爲主線程。

我創造了進口和臨時用途相關的MOCS時也使用相同的模式。

+1

我會老老實實地在'NSLog'後添加一個'abort()'來確保這個問題被捕獲爲一個應用程序沒有正確初始化時無用的時間的99.999%。 –

+0

@ MarcusS.Zarra - 同意。 –

+0

感謝您的接觸,這似乎是最可能的原因。雖然有趣的是,它開始只發生在iOS9上,我也對[NSPersistentStoreCoordinator _coordinator_you_never_successfully_opened_the_database_device_locked:]意味着什麼感興趣。 – AWillian

2

從蘋果開發論壇here這也解釋了一個可能的原因和解決方案的回答。

原因是在描述符中:您的應用程序試圖在設備被鎖定時打開持久性存儲,並且您的代碼在數據保護API上觸發。

這就是你絆倒在新設備和新的iOS版本,因爲新設備的速度足以讓你的應用程序中的數據保護代碼之前運行與過渡做之類的事情。

解決這種問題可以像下面

你很可能進入你的啓動時的代碼,以便它檢查是否受保護的數據是可用的重寫,然後延遲您的Core Data啓動,直到applicationProtectedDataDidBecomeAvailable。