多线程操作数据库 (CoreData)
转:http://linwwwei.iteye.com/blog/1296559
1: 主线程修改了数据库的某一条记录,但是子线程没有发生变化,反过来一样的问题。这种情况一般是发生在app有多个NSManagedObjectContext,两个线程分别对其进行了读写操作。 
 
2: 有时候程序会莫名其妙的crash掉,这个有很多原因: 
 a: 有时候是因为两个线程同时读写数据库中的同一条记录。 
 b: 有时候根本找不到是哪里的原因。 
 这两种情况一般是发生在app只有一个NSManagedObjectContext,两个线程都对其进行了读写操作。 
 
在实际的开发当中,我遇到了各种各样的问题,如果是多线程操作数据库的话,个人建议: 
1: 最好一个线程对应一个NSManagedObjectContext。如果只有一个NSManagedObjectContext,并且多个线程对其进行操作,回出现许多不清不楚的问题。 
2:在每一个线程对应一个NSManagedObjectContext的时候,尽量一个线程只读写与其对应的context。在其完成操作的时 候通知另外的线程去修改其对应的context。在apple的api中有 NSManagedObjectContextDidSaveNotification, 它可以帮助你通知修改其它的contexts。 
eg: [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(mergeContextChangesForNotification:)  name:NSManagedObjectContextDidSaveNotification  object:childThreadManagedObjectContext]; 
 
- (NSManagedObjectContext*)childThreadContext 
{ 
 if (childThreadManagedObjectContext != nil) 
 { 
 return childThreadManagedObjectContext; 
 } 
 
 NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator]; 
 if (coordinator != nil)  
 { 
 childThreadManagedObjectContext = [[NSManagedObjectContext alloc] init]; 
 [childThreadManagedObjectContext setPersistentStoreCoordinator:coordinator]; 
 } 
 else 
 { 
 DLog(@"create child thread managed object context failed!"); 
 } 
 
 [childThreadManagedObjectContext setStalenessInterval:0.0]; 
 [childThreadManagedObjectContext setMergePolicy:NSOverwriteMergePolicy]; 
 
 //////init entity description. 
 pChildThreadEntityDec = [NSEntityDescription  entityForName:@"student"  inManagedObjectContext:childThreadManagedObjectContext]; 
 if (pChildThreadEntityDec == nil) 
 { 
 DLog(@"error init entity description!"); 
 } 
 
 [[NSNotificationCenter defaultCenter] addObserver:self  selector:@selector(mergeContextChangesForNotification:)  name:NSManagedObjectContextDidSaveNotification  object:childThreadManagedObjectContext]; 
 
 return childThreadManagedObjectContext; 
} 
 
- (void)mergeOnMainThread:(NSNotification *)aNotification 
{ 
 [[self managedObjectContext] mergeChangesFromContextDidSaveNotification:aNotification]; 
} 
 
- (void)mergeContextChangesForNotification:(NSNotification *)aNotification 
{ 
 [self performSelectorOnMainThread:@selector(mergeOnMainThread:) withObject:aNotification waitUntilDone:YES]; 
} 
上面的代码只是简单利用NSManagedObjectContextDidSaveNotification,当子线程修改了数据库以后,通知 主线程去修改其对应的context。当然childThreadManagedObjectContext的创建是在创建子线程的时候进行的。 
 
- (void)startChildThread 
{ 
 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 
 NSRunLoop* runLoop = [NSRunLoop currentRunLoop]; 
 
 if ([[manageDatabase sharedInstance] childThreadContext] == nil) 
 { 
 DLog(@"create child thread context failed."); 
 } 
 
 [NSTimer scheduledTimerWithTimeInterval:10000.0 target:self selector:@selector(printCurrentData) userInfo:nil repeats: YES]; 
 
 
 [runLoop run]; 
 [pool release]; 
} 
 
注:实际上在什么时候创建都可以,只要保证在第一次用的之前创建好就可以了。上面是 childThreadManagedObjectContext发生改变时,通知主context修改,所以在修改数据库时,一般都是修改 childThreadManagedObjectContext。 
eg: 
- (void)saveDatabase:(student*)inObject withAge:(NSNumber *)inAge 
{ 
 if (inObject==nil || inAge==nil) 
 { 
 return; 
 } 
 NSError* pError; 
 
 if (self.age != nil) 
 { 
 [self.age release]; 
 self.age = nil; 
 } 
 self.age = [[NSNumber alloc] initWithInt:[inAge intValue]]; 
 
 if ([NSThread currentThread] == [NSThread mainThread]) 
 { 
 [self performSelector:@selector(saveWithChildThread:)  onThread:[dataBaseAppDelegate  shareDelegate].viewController.pShowController.pChildThread  withObject:inObject waitUntilDone:NO]; 
 } 
 else 
 { 
 inObject.stuAge = inAge; 
 if (![childThreadManagedObjectContext save:&pError]) 
 { 
 NSLog(@"save failed: %@, %@", pError, [pError userInfo]); 
 } 
 } 
} 
 
- (void)saveWithChildThread:(student*)inStudent 
{ 
 NSError* pError; 
 
 NSManagedObjectID* tempObjectID = [inStudent objectID]; 
 student* tempStudet = (student*)[childThreadManagedObjectContext objectWithID:tempObjectID]; 
 tempStudet.stuAge = age; 
 
 if (![childThreadManagedObjectContext save:&pError]) 
 { 
 NSLog(@"save failed: %@, %@", pError, [pError userInfo]); 
 } 
} 
这里修改数据库都是通过saveWithChildThread去修改的。当然上面 if ([NSThread currentThread]  == [NSThread  mainThread]),如果时在主线程也可以直接调用saveWithChildThread,而不用放到子线程修改,这里只是为了统一。每个 context只在其对应的线程中修改。
posted on 2012-03-12 11:50 kiao295338444 阅读(1133) 评论(1) 收藏 举报
                    
                
                
            
        
浙公网安备 33010602011771号