首页 > 代码库 > 多线程在Core Data中的使用

多线程在Core Data中的使用

我们知道,Core Data是线程不安全的。我们不能在不同的线程中共享同一个NSManagedObject和NSManagedObjectContext对象。NSManagedObjectContext对象的创建和使用必须在同一个线程中。


当我们使用NSOperation来实现对Core Data的多线程操作的时候,这里要注意的是NSOperation 的init方法是在调用线程执行的,而start和main方法才是在NSOperation所在线程中执行的,因此要在子线程中创建NSManagedObjectContext的时候,必须要在start或者main方法中执行,否则就会出现线程安全问题。


// 官方文档

You must create the managed context on the thread on which it will be used. If you use NSOperation, note that its init method is invoked on the same thread as the caller. You must not, create a managed object context for the queue in the queue‘s init method, otherwise it si associated with the caller‘s thread. Instead, you should create the context in main (for a serial queue) or start (for a concurrent queue).


Using thread confinement, you should not pass managed objects or managed object contexts between thread. To "pass" a managed object from one context another thread boundaries, you either:


1、Pass its object ID (objectID) and use objectWithID: or existingObjectWithID:error: on the receiving managed object context.


    The corresponding managed objects must have been saved - you cannot pass the ID of a newly-inserted managed object to another context.


2、Execute a fetch on the receiving context.



举例如下:

// The main function for this NSOperation, to start the parsing.

- (void)main {

    // Creating context in main function here make sure the context is tied to 

    // current thread.

    // init: use thread confine model to make things simpler.

    self.managedObjectContext = [[NSManagedObjectContext alloc] init];

    self.managedObjectContext.persistentStoreCoordinator = self.sharedPSC;


}


当在Core Data中使用了多线程,就会遇到两个线程中的context的交互问题。我们就使用NSManagedObjectContext的notifications,来实现不同线程的交互

1、NSManagedObjectContextDidSaveNotification


2、NSManagedObjectContextDidChangeNotification


比如有两个线程A,B。A为后台线程,B为主线程。

方法一:使用contextDidSave notification来实现交互,方法如下:

在主线程B中注册NSManagedObjectContextDidSaveNotification,在后台线程A中作相关的操作:增、删、改、查操作。结束之后,save后台线程中的context。后台context会发送一个save的通知。主线程会得到这个通知,由于需要将变化和并到主线程的context中,可以调用主线程context的mergeChangesFromNotification方法,完成之后刷新UI。


这里要注意一个问题,由于通知的接收是在通知的发送线程中,并且context是线程不安全的,因此在收到通知之后,需要dispatch到主线程中,才能去merge changes。举例如下:


// 在主线程中注册通知

// observe the ParseOperation‘s save operation with its managed object context.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:nil];


// 在主线程中处理通知

// this is called via observing "NSManagedObjectContextDidSaveNotification" from

// our APLParseOperation

- (void)mergeChanges:(NSNotification *)notification {

    if(notification.object != self.managedObjectContext) {

        // 由于通知是在后台线程中执行的,context又是线程不安全的,因此需要将更新方法

        // dispatch到主线程中去。

        [self performSelectorOnMainThread:@selector(updateMainContext:) withObject:notification waitUntilDone:NO];

    }

}


// merge changes to main context, fetchedRequestController will automatically monitor

// the changes and update tableview.

- (void)updateMainContext:(NSNotification *)notification {

    assert([NSThread isMainThread]);

    [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];

}


// mergeChangesFromContextDidSaveNotification所作操作如下:

/*

Merges the changes specified in notification object received from another context‘s NSManagedObjectContextDidSaveNotification into the receiver. This method will refresh any objects which have been updated in the other context, fault in any newly inserted objects, and invoke deleteObject: on those which have been deleted. The developer is only responsible for the thread safety of the receiver.

*/

- (void)mergeChangesFromContextDidSaveNotification:(NSNotification *)notification;




多线程在Core Data中的使用