1

我正在開發一個使用SQLLite核心數據數據庫的iOS應用程序。該應用程序在後臺線程中運行同步循環,後者從Web服務獲取數據並將其寫入數據庫。前景(UI)線程在這種情況下繼續,允許用戶針對數據庫運行搜索。多線程核心數據iOS崩潰

我壓力測試應用程序,它崩潰了。在後臺同步任務運行時,我正在前臺運行對數據庫的搜索。數據庫中有大約10,000條記錄,所以它不是很大。

後臺線程使用NSOperation創建,它在NSOperation的main方法中創建NSManagedObjectContext。

前臺線程使用一個不同的NSManagedObjectContext對象,該對象在appDelegate中被初始化(並被提供)。

後臺同步線程一次向數據庫寫入一千條記錄,然後其managedobjectcontext執行保存。

的的NSOperation主要方法是這樣的:

-(void) main { 

    NSDictionary* dictionary = [ HPSJSON getDictionaryFromData:_data ]; 

    // NEED to create the MOC here and pass to the methods. 
    NSManagedObjectContext* moc = [[NSManagedObjectContext alloc] init]; 
    [moc setUndoManager:nil]; 
    //[moc setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy]; 
    //[moc setMergePolicy:NSOverwriteMergePolicy]; 
    [moc setPersistentStoreCoordinator:getApp().persistentStoreCoordinator]; 

    if (dictionary==nil){ 

     [ getApp().operationManager performSelectorOnMainThread: [ self getFailedFunction ] withObject:nil waitUntilDone:NO ];  
     return; 
    } 

    NSString* rc = [self processData: dictionary andMOC:moc ]; // Writes lots of records to the db and saves the moc 

    // performSelectorOnMainThread invokes a method of the receiver on the main thread using the default mode. 
    // i.e. call the method within HPSOperationManager as specified by the getSuccessFunction of the specialised sub-class 
    [ getApp().operationManager performSelectorOnMainThread: [ self getSuccessFunction ] withObject:rc waitUntilDone:NO ];   

} 

的過程數據的方法(通過主調用)包含了大量的代碼,但這裏是片段,它保存:

@try { 
      NSLog(@"HPSDbOperation%@ about to save Indexes moc",objectName); 

      if (rcHappy==YES) 
      { 
       // register for the moc save notification - this is so that other MOCs can be told to merge the changes 
       [[NSNotificationCenter defaultCenter] 
       addObserver:getApp() 
       selector:@selector(handleDidSaveNotification:) 
       name:NSManagedObjectContextDidSaveNotification 
       object:moc]; 

       NSError* error = nil; 
       if ([moc save:&error] == YES) 
       { 
        NSLog(@"HPSDbOperation%@ Indexes SAVED",objectName); 

       }else { 
        NSLog(@"HPSDbOperation%@ Indexes NOT saved ",objectName); 
       } 

       // unregister from notification 
       [[NSNotificationCenter defaultCenter] 
       removeObserver:getApp() 
       name:NSManagedObjectContextDidSaveNotification 
       object:moc]; 

      } 



     } 
     @catch (NSException * e) { 
      NSLog(@"HPSDbOperationBase save Indexes Exception: %@", e); 
      rcHappy=NO; 
     } 

的appDelegate包含以下方法來處理ManagedObjectContext合併:

- (void)handleDidSaveNotification:(NSNotification*) note 
{ 

    @try { 

     NSLog(@"appDelegate handleDidSaveNotification about to run"); 
     [__managedObjectContext mergeChangesFromContextDidSaveNotification:note]; 
     NSLog(@"appDelegate handleDidSaveNotification did run"); 

    } 
    @catch (NSException * e) { 
     NSLog(@"appDelegate handleDidSaveNotification Exception: %@", e); 
    } 

} 

我讓同步運行,然後通過運行連續搜索強調前臺UI線程。在同步和搜索一分鐘或三分鐘後,應用程序崩潰。它似乎沒有被我的任何try-catch結構所捕獲。 Xcode中的崩潰日誌顯示如下:

libobjc.A.dylib`objc_msgSend: 
0x34d92f68: teq.w r0, #0 
0x34d92f6c: beq 0x34d92faa    ; objc_msgSend + 66 
0x34d92f6e: push.w {r3, r4} 
0x34d92f72: ldr r4, [r0] <-- exception here Thread1 EXC_BAD_ACCESS (Code=1) 
0x34d92f74: lsr.w r9, r1, #2 
0x34d92f78: ldr r3, [r4, #8] 
0x34d92f7a: add.w r3, r3, #8 
0x34d92f7e: ldr r12, [r3, #-8] 
0x34d92f82: and.w r9, r9, r12 
0x34d92f86: ldr.w r4, [r3, r9, lsl #2] 
0x34d92f8a: teq.w r4, #0 
0x34d92f8e: add.w r9, r9, #1 
0x34d92f92: beq 0x34d92fa6    ; objc_msgSend + 62 
0x34d92f94: ldr.w r12, [r4] 
0x34d92f98: teq.w r1, r12 
0x34d92f9c: bne 0x34d9317e    ; objc_msgSendSuper_stret + 34 
0x34d92f9e: ldr.w r12, [r4, #8] 
0x34d92fa2: pop {r3, r4} 
0x34d92fa4: bx  r12 
0x34d92fa6: pop {r3, r4} 
0x34d92fa8: b  0x34d92fb0    ; objc_msgSend_uncached 
0x34d92faa: mov.w r1, #0 
0x34d92fae: bx  lr 

我對iOS和objective-c以及核心數據都很陌生。我有點卡住如何推進這個問題。我如何知道應用程序出錯的位置/原因?任何人都知道爲什麼它可能會崩潰?我已經對核心數據中的多線程做了一些閱讀,我相信通過在NSOperation的主要方法中創建MOC,我遵循指導原則,並且通過使用handleDidSaveNotification,我也正確地合併了我的MOC。

幫助!謝謝。

+0

這看起來更像是一個與內存相關的問題,您可以使用殭屍分析器在儀器中運行測試並檢查它是否找到什麼? – JustSid 2012-07-26 19:03:13

+0

如果我自己運行搜索測試,那很好。如果我自己運行同步,那很好。當我把它們撞在一起時,它就會崩潰。我認爲這並不排除內存問題?我會嘗試在樂器中運行它,謝謝。 – whatdoesitallmean 2012-07-26 19:06:34

+0

好吧,你的崩潰發生在obj_msgSend這是發送消息到一個對象的方法(即調用它的方法),它崩潰與一個EXC_BAD_ACCESS這表明存儲器訪問錯誤,所以我不會排除一個內存相關的問題,但它當然也可以與多線程相關。 – JustSid 2012-07-26 19:11:02

回答

2

多線程核心數據使用的主要規則是託管對象上下文和持久存儲的「線程限制」。這意味着,線程本地管理的對象上下文是安全的,但不能將它們從一個線程傳遞到另一個線程。如果你這樣做,你必須處理你自己的鎖定和同步。

看來您在輔助線程中創建了一個託管對象上下文,然後將它傳遞給主線程。我理解正確嗎?如果是這樣,這可以解釋你崩潰。

一種可能性我想是這樣的:

  1. 後臺線程保存;通知已發送;

  2. 主線程開始合併;

  3. 在合併過程中,後臺線程會執行另一次保存。

而不是「救」在3點,也可能是你在你的processData方法使商務部在不同的狀態下做任何操作。

在我看來,您應該能夠通過您已有的日誌跟蹤輕鬆驗證這種情況是否導致崩潰。

+0

是的 - 那正是我正在做的。但是我需要UI線程'看'後臺線程添加的新記錄,所以我如何將背景更改合併到前臺上下文中?謝謝。 – whatdoesitallmean 2012-07-26 19:24:17

+0

但是,我認爲合併上下文正是NSManagedObjectContextDidSaveNotification的全部內容? – whatdoesitallmean 2012-07-26 19:25:37

+0

事實上,你是對的......你可以試試,並且在之後調用'mergeChanges ...'和'unlock'之前放置' - [NSManagedObjectContext lock]'。 – sergio 2012-07-26 19:38:50

0

您不是使用併發類型創建上下文,或是通過父子上下文關係合併更改,因此您的可能問題是線程問題。上下文不是線程安全的,必須在創建它們的同一線程上進行訪問或變異