在不同的线程中获取和使用NSManagedObject

时间:2016-09-22 07:28:31

标签: ios objective-c core-data objective-c-blocks nsmanagedobject

我在构建代码时遇到了问题。 我view创建并依赖model类。 Model类负责所有计算和逻辑。这意味着它需要计算很多东西,并从Core数据中获取对象。

Open/close Principle

关于enter image description here,在一个线程中获得的每个NSManagedObject都需要在同一个线程中使用。

我的问题是创建对象需要在不同的线程中获取,因为构建模型需要一些时间,但在此之后,我需要从View中获取主线程中的对象及其属性(例如在cellForIndex中)。 ..)

我知道,我不是唯一拥有这种设计的人。其他人如何解决这个问题?

编辑:

使用代码具体化。

假设我们有UIView对象MyUIView和模型对象Model

  

MyUIView

 @interface MyUIView ()

 @property(nonatomic) Model * model;

 @end

 @implementation MyUIView

 - (void) createModel
 {
     // privateManagerContext is context manager created with 
     // NSPrivateQueueConcurrencyType, connected to persistent store
     // and sync with "main thread" manager context with 
     // NSManagedObjectContextDidSaveNotification and NSManagedObjectContextDidSaveNotification

     [self.model createWithManagadContext:privateManagerContext];
 }

 // ASSUME THAT THIS CODE IS CALLED AFTER 
 - (void) getNumberOfSomeProperties
 {
    int number  = [self.model getNumberOfProperties];
 }

 - (void) getProperties
 {
    NSArray *array = [self.model properties]
 }

 // OR WE HAVE TO TRIGGERED SOME LONG CALCULATION

 - (void) triggerLongCalculation
 {
     [self.model longCalculation];
 }

 - (void) afterNotifyModelIsCompletedCalculating
 {
     [self doSomeWork];
     [self getProperties];
     ....
 }
 @end
  

模型

 @interface Model ()

 @property(nonatomic) NSArray * cashedProperties;

 @end

 @implementation MyUIView

 - (void) createWithManagadContext:(NSManagedObjectContext *) privateManagerContext
 {
     dispatch_async(self.model_queue, ^(void){

         // Here we fetch objects and do some calculations
         [self populateModel];

         /// Model is complete
         [Notifications notifyModelIsCompletedCreating:self];
     });
 }


 - (void) longCalculation
 {
     dispatch_async(self.model_queue, ^(void){
        // NO CORE DATA FETCH INVOLVED
        [Notifications notifyModelIsCompletedCalculating:self];
    });
 }

 - (int) getNumberOfProperties
 {
    return self.cashedProperties.count;
 }

 - (NSArray) properties
 {
    NSMutableArray * a = [[NSMutableArray alloc]init];
    for (Property * p in self.cashedProperties) {
        [a addObject:p.name];
    }

    return a;
 }

 @end

所以在这个假设的类中,你将如何处理所有NSManagedObject和NSManagedObjectContext?

编辑2:

我正在使用模式,我在appdelegate中创建了两个托管对象上下文,一个私有和一个main,并在它们之间创建同步。

- (NSManagedObjectContext *)managedObjectContext
{
    if (__managedObjectContext != nil) {
        return __managedObjectContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        __managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
        [__managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return __managedObjectContext;
}

- (NSManagedObjectContext * ) privateQueueContext
{
    if (_privateQueueContext != nil) {
        return _privateQueueContext;
    }

    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil) {
        _privateQueueContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
        [_privateQueueContext setPersistentStoreCoordinator:coordinator];
    }
    return _privateQueueContext;
}


- (id)init
{
    self = [super init];
    if (self) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                               selector:@selector(contextDidSavePrivateQueueContext:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                               object:[self privateQueueContext]];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(contextDidSaveMainQueueContext:)
                                                 name:NSManagedObjectContextDidSaveNotification
                                                   object:[self managedObjectContext]];
    }
    return self;
}

#pragma mark - Notifications

- (void)contextDidSavePrivateQueueContext:(NSNotification *)notification
{
    @synchronized(self) {
        [self.managedObjectContext performBlock:^{

            NSArray* objects = [notification.userInfo valueForKey:NSUpdatedObjectsKey];
            for (NSManagedObject* obj in objects) {
                NSManagedObject* mainThreadObject = [self.managedObjectContext objectWithID:obj.objectID];
                [mainThreadObject willAccessValueForKey:nil];
            }

            [self.managedObjectContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
}

- (void)contextDidSaveMainQueueContext:(NSNotification *)notification
{
    @synchronized(self) {
        [self.privateQueueContext performBlock:^{

            NSArray* objects = [notification.userInfo valueForKey:NSUpdatedObjectsKey];
            for (NSManagedObject* obj in objects) {
                NSManagedObject* mainThreadObject = [self.privateQueueContext objectWithID:obj.objectID];
                [mainThreadObject willAccessValueForKey:nil];
            }

            [self.privateQueueContext mergeChangesFromContextDidSaveNotification:notification];
        }];
    }
}

1 个答案:

答案 0 :(得分:1)

您可以将两个不同的NSManagedObjectContext与父/子配置一起使用。 UI的父级,在主队列中,用于在专用队列中繁重工作的子级。一旦子上下文完成以执行其繁重的工作将保存,其更改将传播到主上下文。您可以在视图控制器中使用表视图的情况下使用在主上下文中观察的NSFetchedResultsController。一旦主上下文接收并合并来自其子上下文的更改,NSFetchedResultsController将相应地更新UI,只要其实现其委托方法即可。

如果您不使用NSFRC,您可以注册通知“name:NSManagedObjectContextObjectsDidChangeNotification”或“name:NSManagedObjectContextObjectsDidSaveNotification”的主要上下文,并查看已添加/删除/更新的对象,并因此更新UI。

如果您使用父级/子级的线程限制(过去几年被Apple淘汰),您可能希望在线程之间传递objectID并在您需要的上下文中获取对象,因为NSManagedObjects不是线程安全的。

代码:

首先,我不会在视图中引用您的模型。视图应该是愚蠢的,并暴露出要填充的插座或方法。我会让ViewController与模型和视图进行通信。

假设您每次需要执行繁重的工作时都有一个util方法来创建工作者上下文(作为父队列的子上下文)。

func newWorkerContext() -> NSManagedObjectContext {
    let workerContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
    workerContext.parentContext = self.mainContext
    return workerContext
}

在你的模型中你会有

//Lets say you have a reference to your workerContext
func longCalculation() {
    workerContext.performBlock({ () -> Void in
          //Heavy process of information
          //Once finished you save the worker context and changes are propagated to the parent context    
    })

}

在你的ViewController中你会有

class MyViewController: UIViewController {

   func viewDidLoad() {
      super.viewDidLoad()

      NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(handleDataModelChange(_:)), name: NSManagedObjectContextObjectsDidChangeNotification, object: REFERENCE_TO_MAIN_CONTEXT)
   }

  func deinit {   
      NSNotificationCenter.defaultCenter().removeObserver(self, name: NSManagedObjectContextObjectsDidChangeNotification, object: REFERENCE_TO_MAIN_CONTEXT)  
  }

func handleDataModelChange(notification: NSNotification) {

    //Check changes are relevant for the UI you are updating and if so update.
    if let changedObjects = changes[NSUpdatedObjectsKey] as? NSSet {
    }
    if let insertedObjects = changes[NSInsertedObjectsKey] as? NSSet {
    }
    if let deletedObjects = changes[NSDeletedObjectsKey] as? NSSet {
    }
}
}

请记住,要在持久性存储中保留更改,您必须保存在主上下文中。 这是一个简单的例子,但我希望它能让你了解现在该做什么。