iOS App插入数千条CoreData数据时崩溃(EXC_BAD_ACCESS Code 1)
解决CoreData同步数千条记录时的EXC_BAD_ACCESS崩溃问题
首先,你的崩溃问题大概率是违反了CoreData的并发访问规则导致的,结合代码来看,主要有几个关键问题需要修正:
1. PrivateQueue上下文必须在私有队列内操作
你创建的是NSPrivateQueueConcurrencyType类型的上下文,但现在直接在全局并发队列(dispatch_get_global_queue)里访问这个上下文并执行插入、保存操作,这是完全错误的。CoreData要求PrivateQueue类型的上下文,所有的操作(包括创建NSManagedObject、保存上下文等)都必须通过它自己的私有队列来执行,也就是使用performBlock:或performBlockAndWait:方法。
直接在外部队列访问PrivateQueue上下文,会导致线程冲突,引发EXC_BAD_ACCESS这类内存错误,尤其是在处理大量数据时,线程竞争的概率更高。
2. 代码修正方案
第一步:修正后台上下文的使用方式
把你在全局队列里的CoreData操作,全部放到上下文的performBlock:里执行:
-(void)RestClient:(RestClient *)client didCompleteWithSuccess:(id)responseObject withLastModifiedDate:(NSString *)lastModifiedDate{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { @try { if(responseObject!=nil){ NSError *error = nil; NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:&error]; if (!error) { int totalrecords = [[jsonResponse objectForKey:@"records"] intValue]; if(totalrecords>0){ NSManagedObjectContext *manageObjContext = [Common getBkgManagedObjectContext]; NSArray* rows = [jsonResponse objectForKey:@"rows"]; // 将CoreData操作放到上下文的私有队列中执行 [manageObjContext performBlock:^{ for(NSString* email in rows){ [NotifyEmails insertIntoNotifyEmails:email withManagedObjectContext:manageObjContext]; } NSError *saveError = nil; if (![manageObjContext save:&saveError]) { NSLog(@"Data couldn't save: %@", [saveError localizedDescription]); } }]; } } } }@catch (NSException *exception) { NSLog(@"exception in rest response"); NSLog(@"Error :: %@",exception); }@finally { [self finishOperation]; } }); }
第二步:优化后台上下文的创建(可选但推荐)
你的getBkgManagedObjectContext方法每次调用都会创建一个新的上下文,虽然不是崩溃的直接原因,但频繁创建上下文会浪费资源。建议改为复用一个全局的后台上下文:
// 在Common类中添加一个静态变量存储后台上下文 static NSManagedObjectContext *_bkgManagedObjectContext = nil; - (NSManagedObjectContext *)getBkgManagedObjectContext { @synchronized(self) { if (_bkgManagedObjectContext == nil) { NSPersistentStoreCoordinator *coordinator = [(EMAINTAINAppDelegate *)[[UIApplication sharedApplication] delegate] persistentStoreCoordinator]; if (coordinator != nil) { _bkgManagedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; [_bkgManagedObjectContext setPersistentStoreCoordinator:coordinator]; // 可以设置自动合并变更,方便和主上下文同步 _bkgManagedObjectContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy; } } } return _bkgManagedObjectContext; }
第三步:大量数据插入的内存优化(避免后续内存问题)
当插入数千条记录时,一次性插入可能导致内存占用过高。可以考虑分批插入并保存,比如每插入100条就保存一次上下文,然后重置上下文来释放内存:
[manageObjContext performBlock:^{ NSInteger batchSize = 100; NSInteger totalCount = rows.count; for (NSInteger i = 0; i < totalCount; i++) { NSString* email = rows[i]; [NotifyEmails insertIntoNotifyEmails:email withManagedObjectContext:manageObjContext]; // 每batchSize条记录保存一次 if ((i + 1) % batchSize == 0 || (i + 1) == totalCount) { NSError *saveError = nil; if ([manageObjContext save:&saveError]) { // 保存后重置上下文,释放已插入的对象占用的内存 [manageObjContext reset]; } else { NSLog(@"Batch save failed: %@", [saveError localizedDescription]); } } } }];
3. 核心原理回顾
CoreData的并发模型要求:
NSPrivateQueueConcurrencyType上下文:必须通过performBlock:/performBlockAndWait:在其私有队列执行所有操作NSMainQueueConcurrencyType上下文:只能在主线程访问,或者通过performBlock:在主线程执行操作- 不同上下文之间的对象不能直接跨线程访问,必须通过
objectID来传递
违反这些规则就会导致线程冲突,出现EXC_BAD_ACCESS、数据损坏等问题。
内容的提问来源于stack exchange,提问作者Sanjay L
相关产品推荐
相关产品推荐

