【问题标题】:NSManagedObject as MKAnnotation and Core Data ConcurrencyNSManagedObject 作为 MKAnnotation 和核心数据并发
【发布时间】:2015-03-20 10:36:56
【问题描述】:

我正在使用我认为是相当典型的NSManagedObject 子类实现,它符合MKAnnotation 协议,以便在MKMapView 中显示。查看 setter 和 getter:

-(CLLocationCoordinate2D)coordinate {
    CLLocationCoordinate2D coord = EMPTY_LOCATION_COORDINATE;
    BOOL validLong = (self.longitude != nil) && ([self.longitude doubleValue] != 0);
    BOOL validLat = (self.latitude != nil) && ([self.latitude doubleValue] != 0);
    if (validLong && validLat) {
        coord.longitude = [self.longitude doubleValue];
        coord.latitude = [self.latitude doubleValue];
    }

    return coord;
}

-(void)setCoordinate:(CLLocationCoordinate2D)coordinate {
    if (coordinate.latitude != EMPTY_LOCATION && coordinate.longitude != EMPTY_LOCATION) {
        self.latitude = [NSNumber numberWithDouble:coordinate.latitude];
        self.longitude = [NSNumber numberWithDouble:coordinate.longitude];
    } else {
        self.latitude = nil;
        self.longitude = nil;
    }
}

-(NSString *)title {
    NSString *str = [self.projectName copy];
    return str;
}

这是有效的,根本不会在生产中引起问题。

我正在使用 Core Data 多线程断言调试一些 Core Data 并发问题,我发现它会将 gutter 标记为并发违规。我的猜测是调用坐标的MKMapview 正在使用后台线程,从技术上讲这是不允许的。可以想象,它不能保证它在生产中工作。

我尝试将 getter 包装在 [self.managedObjectContext performBlockAndWait^(void){ //set here }]; 块中,但这会导致线程锁定失败。

我应该忽略错误并继续前进,还是为此目的有更好的做法?

【问题讨论】:

  • 您遇到的确切错误是什么?您可以在 getter 中放置一个断点来检查它被调用的线程。另外,您的托管对象所在的NSManagedObjectContext 是什么concurrencyType
  • 我没有收到错误消息,它只是在给自己的第一条消息上中断。我相信 moc 的并发类型是主队列,但现在你提出来了,我需要检查一下。有可能我在某个时候改变了这一点而没有考虑这个问题。

标签: ios objective-c core-data thread-safety mkannotation


【解决方案1】:

我不确定我的解决方案是否存在问题,但制作代理对象对我来说太复杂了。我已经对其进行了测试,并且在启用-com.apple.CoreData.ConcurrencyDebug 选项的情况下似乎不会出现线程冲突。因为我知道我的NSManagedObjectContext 在主队列中,所以我这样做了:

- (NSString *) title {
    if( [NSThread isMainThread] ) {
        return [self.projectName copy];
    } else {
        __block NSString *title;
        NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
        context.persistentStoreCoordinator = self.managedObjectContext.persistentStoreCoordinator;
        [context performBlockAndWait: ^{
            ActivityLocation *tmp = [context objectWithID: self.objectID];
            title = [tmp.projectName copy];
        }];
        return title;
    }
}

我对@9​​87654324@ 和subtitle 以及任何其他导致问题的方法做了同样的事情。

显然有一些与主队列相关的“等待”操作,因为如果我将新创建的上下文的父级设置为 'self 的上下文,它会死锁。

有趣的是,即使新创建的上下文在所需的线程上,如果没有performBlockAndWait:,它也无法工作,即使我打印带有或不带performBlockAndWait: 的当前线程,它也会打印同样的东西。

如果对象的 projectName 属性中有未保存的更改,则将显示旧值,因为 persistentStoreCoordinator 用于新创建的上下文,但我可以控制它,不像我没有控制 Apple 在哪个线程上调用我的 MKAnnotation 方法。

我希望这对某人有用,即使我是在回答六年前提出的问题!

【讨论】:

    【解决方案2】:

    首先,坐标应该被实现为一个瞬态,包括一个神奇的 keyPathsForValuesAffectingCoordinate 方法。为简单起见,您可以从未缓存的瞬态开始,然后在必要时添加额外的代码来缓存它。

    其次,应使用 Core Data 进行验证。 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/ObjectValidation.html#//apple_ref/doc/uid/TP40001075-CH20-SW1

    【讨论】:

      【解决方案3】:

      我找不到原因。我验证了 NSManagedObject 在主队列上下文中。正在询问不是主队列的队列上的坐标。我所做的修复是使用代理对象作为传递给 MKMapview 的注释,而不是直接传递它。 NSObject 类符合 MKAnnotation 协议。使用我的 NSManagedObject 中的坐标和标题进行初始化,传递它而不是真正的交易。

      【讨论】:

      • MapKit 在自己的线程上做事,这很棒,因为它的速度很快并且可以保持响应,但这意味着它不能 100% 兼容像 Core Data 这样的非线程安全 API。
      • 我可以确认它发生在我身上的原因完全相同。解决方案正如您所说:从 NSManagedObject 实例创建代理对象。
      猜你喜欢
      • 1970-01-01
      • 2011-05-12
      • 1970-01-01
      • 2018-01-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多