coreData 深入理解3 (iOS5 以前线程安全与同步)

原创 2014年03月24日 15:20:55
iOS5 以前采用方法:

1: 主线程修改了数据库的某一条记录,但是子线程没有发生变化,反过来一样的问题。这种情况一般是发生在app有多个NSManagedObjectContext,两个线程分别对其进行了读写操作。

2: 有时候程序会莫名其妙的crash掉,这个有很多原因:
          a: 有时候是因为两个线程同时读写数据库中的同一条记录。
          b: 在Core Data的多线程环境中,我们只能传递objectID或者重新fetch, 而我们传递了object
          c: 有时候根本找不到是哪里的原因。

          这两种情况一般是发生在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;
 
  // 传递objectID !!!
    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只在其对应的线程中修改。
(iOS5 以后iOS 对此引入parent context conception, 就可用不用注册通知了,参加coreData 深入理解2)


coreData 深入理解4 --总结 (线程安全与同步--iOS5 前后对比)

目录(?)[+] Core Data是iOS中很重要的一个部分,可以理解为基于SQLite(当然也可以是其他的Storage,如In-memory,只是SQLite比较常见)的一个ORM实现...

coreData 深入理解4 --总结 (线程安全与同步--iOS5 前后对比)

Core Data是iOS中很重要的一个部分,可以理解为基于SQLite(当然也可以是其他的Storage,如In-memory,只是SQLite比较常见)的一个ORM实现,所以有关系数据库的特性,又...

the-ios5-developer-cookbook-3rd-edition.pdf

  • 2012年09月23日 15:58
  • 5.44MB
  • 下载

IOS5编程-3-第一个工程

第一个工程 打开Xcode,你会看到下面这个界面: 一般情况下,我们会去除Show this window when Xcode launches。然后点击Cancel移除这个界面。在菜单...

[iOS开发兼容问题之]关于同步IOS6和IOS5旋屏问题

关于同步ios6和ios5旋屏问题 相信大家都知道,ios6并不支持 shouldAutorotateToInterfaceOrientation 而强制打开项目的所有方向旋屏,会给一部分项目带...

iOS5编程--官方例子代码的研究--2.UICatalog-3

6.TextViewController类 这里我们第一个关注的函数就是setupTextView 我把相应的注释写在下面,类似的其他的就不再解释。 self.textView = ...

iOS5编程--官方例子代码的研究--3. TableViewSuite--2_SimpleSectionedTableView

关于table view,分为两种方式,一种是UITableViewStylePlain,另一种是UITableViewStyleGrouped。两种模式都把行row分为若干个节section。每个s...

iOS5编程--官方例子代码的研究--3. TableViewSuite--1_SimpleTableView

UITableView是很重要的一个View了,这个话题有点大,因此我这里只是简单的分析这个例子给我们提供的信息。关于其他的,我会放在中级或者高级话题里面讲述。 类SimpleTableViewAp...

iOS5编程--UIApplication深入研究

很多时候,我们不需要关心这个类,我们很少继承这个类,偶尔会调用这个类的api来实现一些功能,但是不可否认,这个类是iOS编程中很重要的一个概念,所以我这里写这个文章来总结以下这个类的信息,如果写的不对...

【wonderful】iOS5编程--UIApplication深入研究

很多时候,我们不需要关心这个类,我们很少继承这个类,偶尔会调用这个类的api来实现一些功能,但是不可否认,这个类是iOS编程中很重要的一个概念,所以我这里写这个文章来总结以下这个类的信息,如果写的不对...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:coreData 深入理解3 (iOS5 以前线程安全与同步)
举报原因:
原因补充:

(最多只允许输入30个字)