多线程核心数据iOS崩溃

时间:2012-07-26 18:58:22

标签: objective-c ios core-data nsmanagedobjectcontext nsoperation

我正在开发一个使用SQLLite核心数据db的iOS应用程序。该应用程序在后台线程中运行同步循环,该线程从Web服务获取数据并将其写入数据库。前景(UI)线程在发生这种情况时继续,允许用户对数据库运行搜索。

我正在对应用程序进行压力测试,但它正在崩溃。我正在运行后台同步任务时对前台的数据库运行搜索。数据库中有大约10,000条记录,因此它并不大。

后台线程是使用NSOperation创建的,它在NSOperation的main方法中创建了一个NSManagedObjectContext。

前台线程使用在appDelegate内初始化(并由其提供)的不同NSManagedObjectContext对象。

后台同步线程一次将一千条记录写入数据库,然后其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 ];        

}

processData方法(由main调用)包含大量代码,但这里是执行保存的代码段:

@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。

帮助!谢谢。

2 个答案:

答案 0 :(得分:2)

多线程Core Data使用的主要规则是托管对象上下文和持久存储的“线程限制”。这意味着,拥有线程本地托管对象上下文但不将它们从一个线程传递到另一个线程是安全的。如果这样做,您必须处理自己的锁定和同步。

似乎您在辅助线程中创建了一个托管对象上下文,然后将其传递给主线程。我理解正确吗?如果是这样,这可以解释你崩溃。

我想到的一个可能性是:

  1. 后台线程保存;通知已发送;

  2. 主线程开始合并;

  3. 当合并正在进行时,后台线程会进行另一次保存。

  4. 而不是在第3点“保存”,它可能是您在processData方法中执行的任何操作,它将moc置于不同的状态。

    在我看来,您应该能够通过已有的日志跟踪轻松验证这种情况是否是导致崩溃的原因。

答案 1 :(得分:0)

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