1

I am developing an iOS app that uses a SQLLite core data db. The app runs a sync loop in a background thread that grabs data from a web service and writes it to the db. The foreground (UI) thread continues while this happens, allowing the user to run searches against the db.

I am stress-testing the app and it is crashing. I am running searches against the db in the foreground while the background sync task runs. There are approx 10,000 records in the db, so it is not huge.

The background thread is created using NSOperation and it creates an NSManagedObjectContext in the main method of the NSOperation.

The foreground thread uses a different NSManagedObjectContext object initialised within (and made available by) the appDelegate.

The background sync thread writes a thousand records at a time to the db, then its managedobjectcontext performs a save.

The NSOperation main method looks like this:

-(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 ];        

}

The processData method (called by main) contains lots of code, but here is the snippet that does the save:

@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;
        }

The appDelegate contains the following method to handle the ManagedObjectContext merge:

- (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);
    }

}

I let the sync run, and then I stress the foreground UI thread by running successive searches. After a minute or three of syncing and searching, the app crashes. It doesn't seem to get caught by any of my try-catch constructs. The crash log in Xcode shows the following:

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

I am very new to iOS and objective-c and core-data. I'm a bit stuck on how to progress this issue. How can I tell exactly where/why the app is going wrong? Anyone have any idea why it might be crashing? I have done a bit of reading on multi-threading in core data and I believe that by creating the MOCs in the main method of NSOperation I am following guidelines, and by using handleDidSaveNotification I am also merging my MOCs correctly.

Help! Thank you.

4

2 回答 2

2

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

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

我正在考虑的一种可能性是:

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

  2. 主线程开始合并;

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

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

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

于 2012-07-26T19:11:56.637 回答
0

You aren't creating the context with a concurrency type, or merging changes through a parent child context relationship, so your likely problem is a threading issue. Contexts are not thread-safe, and must be accessed or mutated on the same thread that created them

于 2012-07-26T19:12:53.610 回答