Objective c 多线程核心数据iOS崩溃
我正在开发一个iOS应用程序,它使用SQLLite核心数据数据库。该应用程序在后台线程中运行同步循环,从web服务获取数据并将其写入数据库。发生这种情况时,前台(UI)线程继续运行,允许用户对db运行搜索 我正在对应用程序进行压力测试,它正在崩溃。当后台同步任务运行时,我正在前台对数据库进行搜索。数据库中大约有10000条记录,所以不是很大 后台线程是使用NSOperation创建的,它在NSOperation的主方法中创建NSManagedObjectContext 前台线程使用在appDelegate中初始化(并由appDelegate提供)的不同NSManagedObjectContext对象 后台同步线程一次向数据库写入1000条记录,然后其managedobjectcontext执行保存 NSOperation main方法如下所示:Objective c 多线程核心数据iOS崩溃,objective-c,ios,core-data,nsmanagedobjectcontext,nsoperation,Objective C,Ios,Core Data,Nsmanagedobjectcontext,Nsoperation,我正在开发一个iOS应用程序,它使用SQLLite核心数据数据库。该应用程序在后台线程中运行同步循环,从web服务获取数据并将其写入数据库。发生这种情况时,前台(UI)线程继续运行,允许用户对db运行搜索 我正在对应用程序进行压力测试,它正在崩溃。当后台同步任务运行时,我正在前台对数据库进行搜索。数据库中大约有10000条记录,所以不是很大 后台线程是使用NSOperation创建的,它在NSOperation的主方法中创建NSManagedObjectContext 前台线程使用在appDel
-(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
libobjc.A.dylib`objc\u msgSend:
0x34d92f68:teq.w r0,#0
0x34d92f6c:beq 0x34d92faa;objc_msgSend+66
0x34d92f6e:push.w{r3,r4}
0x34d92f72:ldr r4,[r0]多线程核心数据使用的主要规则是托管对象上下文和持久存储的“线程限制”。这意味着,使用线程本地托管对象上下文是安全的,但不能将它们从一个线程传递到另一个线程。如果这样做,您必须自行处理锁定和同步
似乎您在次线程中创建了一个托管对象上下文,然后将其传递给主线程。我理解正确吗?如果是这样,这可以解释你撞车的原因
我想到的一种可能性是:
后台线程保存;发送通知
主线程开始合并
合并正在进行时,后台线程执行另一个保存
不是在第3点“保存”,而是在processData
方法中执行的使moc处于不同状态的任何操作
在我看来,您应该能够通过已有的日志跟踪轻松验证此事件是否是崩溃的原因。您没有创建具有并发类型的上下文,也没有通过父子上下文关系合并更改,因此您可能的问题是线程问题。上下文不是线程安全的,必须在创建上下文的同一个线程上访问或修改上下文这在我看来更像是一个与内存相关的问题,你能用zombie profiler在Instruments中运行测试并检查它是否找到了什么吗?如果我自己运行搜索测试就可以了。如果我自己运行同步,就可以了。当我把它们放在一起运行时,它崩溃了。我想这不排除内存问题吧?我会按照你的建议尝试在仪器中运行它-谢谢。好吧,你的崩溃发生在obj_msgSend中,它是一种向对象发送消息的方法(即调用对象上的方法),它会与EXC_BAD_访问崩溃,这表明内存访问错误,所以我不排除内存相关的问题,但它当然也可能与多线程相关。这可能不会有帮助,但有可能……尝试使用Xcode断点导航器底部的“+”符号在Objective-C异常上设置断点。它有时会导致调试器提供更多有用的信息。是的,这正是我正在做的。但是我需要UI线程“查看”后台线程添加的新记录,那么如何将后台更改合并到前台上下文中呢?谢谢。不过,我认为合并上下文正是NSManagedObjectContextDidSaveNotification的核心所在?事实上,你是对的。。。您可以在调用mergeChanges…
和unlock
之前尝试将-[NSManagedObjectContext lock]
放入。我尝试将我的mergeChangesFromContextDidSaveNotification包装在try lock/unlock中,但在一两分钟的测试后(测试期间一切正常),应用程序仍然崩溃。