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

您不是使用并发类型创建上下文,或是通过父子上下文关系合并更改,因此您的可能问题是线程问题。上下文不是线程安全的,必须在创建它们的同一线程上进行访问或变异