2017-08-14 104 views
4

當我在其他線程中仍在運行更新事件時嘗試刪除數據庫時,我在Realm上有一個不斷的崩潰。當更新失效時,領域崩潰

崩潰是:

2017-08-14 18:07:56.289 App Staging[28264:7828070] *** Terminating app due to uncaught exception 'RLMException', reason: 'Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.' 
*** First throw call stack: 
(
    0 CoreFoundation      0x000000010c67fb0b __exceptionPreprocess + 171 
    1 libobjc.A.dylib      0x000000010c0e4141 objc_exception_throw + 48 
    2 Realm        0x0000000108095f96 _ZL27RLMVerifyInWriteTransactionP8RLMRealm + 86 
    3 Realm        0x000000010809710a RLMCreateObjectInRealmWithValue + 138 
    4 Realm        0x00000001080820af +[RLMObject createOrUpdateInRealm:withValue:] + 607 
    5 App Staging       0x0000000107497ae0 +[RealmRoundable createOrUpdateInRealm:withMemberResponse:] + 400 
    6 App Staging       0x0000000107497916 +[RealmRoundable createOrUpdateWithMemberResponse:] + 118 
    7 App Staging       0x000000010742a0a0 +[RealmStaff createOrUpdateInRealm:withResponse:inCareProvider:] + 352 
    8 App Staging       0x000000010742ab92 +[RealmStaff createOrUpdateInRealm:withStaff:inCareProvider:] + 514 
    9 App Staging       0x000000010742a94b +[RealmStaff createOrUpdateStaff:inCareProvider:] + 139 
    10 App Staging       0x000000010733b803 -[StaffRoundableTableViewController updateRoundables:fromDataLoader:inCareProvider:] + 131 
    11 App Staging       0x000000010747ae3a __54-[RoundableTableViewController dataLoaderDidLoadData:]_block_invoke.324 + 122 
    12 Realm        0x00000001081d01a6 -[RLMRealm transactionWithBlock:error:] + 86 
    13 Realm        0x00000001081d010e -[RLMRealm transactionWithBlock:] + 62 
    14 App Staging       0x000000010747ab0d __54-[RoundableTableViewController dataLoaderDidLoadData:]_block_invoke + 765 
    15 libdispatch.dylib     0x000000010e15c4a6 _dispatch_call_block_and_release + 12 
    16 libdispatch.dylib     0x000000010e18505c _dispatch_client_callout + 8 
    17 libdispatch.dylib     0x000000010e164dcd _dispatch_queue_override_invoke + 1321 
    18 libdispatch.dylib     0x000000010e166ec4 _dispatch_root_queue_drain + 634 
    19 libdispatch.dylib     0x000000010e166bef _dispatch_worker_thread3 + 123 
    20 libsystem_pthread.dylib    0x000000010e51c5a2 _pthread_wqthread + 1299 
    21 libsystem_pthread.dylib    0x000000010e51c07d start_wqthread + 13 
) 
libc++abi.dylib: terminating with uncaught exception of type NSException 

發生這種情況時,我打電話:

[RealmManager deleteRealm]; 

這是爲實現:

+ (void)deleteRealm 
{ 
    @autoreleasepool { 
     [[RLMRealm defaultRealm] invalidate]; 
     // Hack to force Realm to clear cache because config is cached and crashes eventually because it detects encryption key has changed 
     SUPPRESS_UNDECLARED_SELECTOR_WARNING([[RLMRealm class] performSelector:@selector(resetRealmState)]); 
    } 

    NSFileManager *manager = [NSFileManager defaultManager]; 
    RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration]; 
    NSArray<NSURL *> *realmFileURLs = @[ 
             config.fileURL, 
             [config.fileURL URLByAppendingPathExtension:@"lock"], 
             [config.fileURL URLByAppendingPathExtension:@"log_a"], 
             [config.fileURL URLByAppendingPathExtension:@"log_b"], 
             [config.fileURL URLByAppendingPathExtension:@"note"], 
             [[config.fileURL URLByDeletingLastPathComponent] URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.realm.management", [[config.fileURL URLByDeletingPathExtension] lastPathComponent]]] 
             ]; 
    for (NSURL *URL in realmFileURLs) { 
     NSError *error = nil; 
     [manager removeItemAtURL:URL error:&error]; 
     if (error) { 
      // handle error 
      DDLogError(@"Error deleting realm file - %@", error); 
     } 
    } 
} 

我的問題是:有沒有辦法運行此代碼之前,請停止所有Realm操作

回答

4

從境界的文檔上Deleting Realm Files

因爲境界避免數據複製到內存中,除了絕對需要的時候,通過一個域管理的所有對象保存在磁盤上的文件引用,並且必須在文件之前被釋放可以被安全刪除。這包括從Realm讀取(或添加到)的所有對象,全部爲RLMArray,RLMResultsRLMThreadSafeReference對象,以及RLMRealm本身。

實際上,這意味着刪除一個Realm文件應該在應用程序啓動之前打開Realm,或者在顯式自動釋放池中打開Realm之後完成,這將確保所有的Realm對象都具有被釋放。

移除文件時,仍然在另一個線程上訪問它們會導致各種問題。調用Realm的私有方法,例如+[RLMRealm resetRealmState]也會。我強烈建議不要做這些事情。

您可能會以稍微不同的方式來解決此問題,具體取決於從磁盤中刪除Realm文件的動機。如果您可以分享更多關於您的用例的信息,我可能會提供更具體的建議。

例如,您可以跟蹤後臺線程是否正在使用Realm,並且只在閒置時才刪除它。您需要非常小心,以確保在刪除文件的時候,所有對Realm的引用都被刪除,否則最終可能會通過已打開的文件句柄繼續訪問現在已刪除的文件。

或者,不是立即刪除Realm文件,而是爲新的Realm文件生成一個新的唯一路徑。然後,如果您確定應用的其餘部分未使用Realm文件,則會刪除這些Realm文件(下次啓動是實現此目的的一種非常可靠的方式,或者將其綁定到應用生命週期中的某個點你知道舊狀態不能再被訪問)。這是我的偏好,因爲在使用文件時沒有機會刪除文件。對於許多具有登錄用戶概念的應用程序來說,這也非常合適,因爲每個用戶的不同Realm路徑是一個相對容易理解的概念。

+0

當用戶結束會話時,我們根據HIPAA合規性原因刪除Realm。我們曾經使用加密核心數據來做到這一點,但現在我們正在向Realm進行這種大規模的遷移,並通過審計來清除數據庫的所有痕跡。 – ppaulojr

+0

會使用加密的Realm,會話結束時丟棄加密密鑰,然後在稍後清理文件滿足要求嗎? – bdash

+0

我會嘗試出售它。現在我可以通過在刪除Realm文件之前運行此操作來避免崩潰。 – ppaulojr