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.