【问题标题】:Using managed object context with Dispatch queue in Core Data在 Core Data 中使用托管对象上下文和调度队列
【发布时间】:2016-10-14 23:53:06
【问题描述】:

根据苹果的指导方针,为了在主线程上使用托管对象,它们需要由仅限于主线程的上下文获取,好的,没关系。下面是我的代码...

AppDelegate *del = [[UIApplication sharedApplication] delegate];

dispatch_queue_t queue1 = dispatch_queue_create("com.MyApp.AppTask",NULL);
dispatch_queue_t main = dispatch_get_main_queue();
dispatch_async(queue1, ^{
    NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    workerContext.persistentStoreCoordinator =  del.persistentStoreCoordinator;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
    NSArray *managedObject = [workerContext executeFetchRequest:fetchRequest error:nil];

    dispatch_async(main, ^{
        NSLog(@"%@",managedObject);
        Person *objperson = [managedObject objectAtIndex:0];
        objperson.firstname = @“test”;
        BOOL s = [workerContext save:nil];
        if (s) {
            NSLog(@"done");
        }
    });
});

现在根据指南,我无法修改或保存由另一个线程创建的托管对象上下文。但上面的代码工作正常,修改和保存我的对象没有任何错误。因此,我可以修改由另一个线程获取的 MOC,甚至可以保存由另一个线程创建的 MOC。

请让我知道我这样做的方式是否错误,因为理想情况下我无法从主线程中保存后台线程的 MOC。

谢谢。

【问题讨论】:

    标签: ios core-data grand-central-dispatch


    【解决方案1】:

    这是错误的,因为它的线程不安全 NOT 线程不可能跨线程与上下文和托管对象。

    因此,您的琐碎示例可能在某些时候有效,但并非在所有情况下都有效。迟早你会因为这种模式而崩溃。

    如果您希望在线程之间访问对象,您必须通过线程发送objectID

    当您使用NSPrivateQueueConcurrencyType 创建上下文时,它会创建并管理自己的队列。

    你的例子更好地表达为

    AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
    
        NSManagedObjectContext *workerContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
    
        //set the parent NOT the persistent store coordinator
        workerContext.parentContext =  delegate.managedObjectContext;
    
        [workerContext performBlock:^{
    
            NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Person"];
            NSArray *results = [workerContext executeFetchRequest:fetchRequest error:nil];
    
            if (results.count) {
    
                Person *person = [results objectAtIndex:0];
                person.firstname = @“test”;
    
                BOOL success = [workerContext save:nil];
                if (success) {
                    NSLog(@"done");
                }
    
                //you pass ObjectID's NOT managed objects across threads
                NSManagedObjectID *objectID = [person objectID];
    
                dispatch_async(dispatch_get_main_queue(), ^{
    
                    //update your UI here
                    Person *thePerson = (Person *)[[delegate managedObjectContext] objectWithID:objectID];
                    self.myUIElement.text = person.firstname;
    
                });
            }
    
        }];
    

    【讨论】:

    • 为了保持一致,我会设置 [mainManagedObjectContext performBlock:^{(其中 mainManagedObjectContext 使用 concurrencyType .MainQueueConcurrencyType 初始化)而不是 dispatch_async(dispatch_get_main_queue(), ^{
    • objectWithID: 应该在上下文的队列上完成,就像在它之后立即触发故障一样。将那些移出主队列块。
    • @pob true ,OP 正在使用 Xcode 模板堆栈,它隐含地提供了这一点,但您的更改是一个更正确的示例。
    • @qullish 我想要主线程上下文中的对象,以便我可以在主线程上更新 UI。如果您在私有队列中通过 ID 恢复对象,则会导致无法完成的故障和悲伤。
    • @WarrenBurton,不。在主队列调度块之外将局部变量设置为 person.firstname,并将 self.myUIElement.text 设置为块内的该局部变量。我无法在评论中更好地说明这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-10-23
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    相关资源
    最近更新 更多