【问题标题】:Getting and using NSManagedObject in different threads在不同线程中获取和使用 NSManagedObject
【发布时间】:2017-01-30 15:57:11
【问题描述】:

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

关于enter link 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 中创建两个托管对象上下文,一个是私有的,一个是主的,并在它们之间创建同步。

- (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];
        }];
    }
}

【问题讨论】:

  • 您可以在主线程中加载对象,也可以创建对象的非托管版本并在计算完成后将其传递给主线程。

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


【解决方案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 {
    }
}
}

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

【讨论】:

  • 感谢您的回答。理论上我明白你的意思,但如果你能更具体地回答,我会更新我的问题。
  • @MarkoZadravec 你试过我建议的方法了吗??
  • 我看到你的回答了,谢谢。我更新我的问题。我在应用程序委托中使用 NSManagedObjectContextDidSaveNotification 而不是 UIViewController 中的 NSManagedObjectContextObjectsDidChangeNotification。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-19
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多