NSManagedObject 作为 MKAnnotation 和核心数据并发
NSManagedObject as MKAnnotation and Core Data Concurrency
我正在使用我认为是 NSManagedObject
子类的相当典型的实现,它符合 MKAnnotation
协议,以便在 MKMapView
中显示。参见 setter 和 getters:
-(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 }];
块中,但这会导致线程锁定失败。
我应该忽略该错误并继续前进,还是对此有更好的做法?
我找不到原因。我验证了 NSManagedObject 在主 queue 上下文中。它被要求提供 queue 上的坐标,该坐标不是主 queue。我所做的修复是使用代理 object 作为传递给 MKMapview 的注释,而不是直接传递它。符合 MKAnnotation 协议的 NSObject class。使用我的 NSManagedObject 中的坐标和标题进行初始化,传递它而不是真正的交易。
首先,坐标应作为一个瞬态实现,包括一个神奇的 keyPathsForValuesAffectingCoordinate 方法。为简单起见,您可以从未缓存的瞬态开始,然后在必要时添加额外的代码来缓存它。
其次,应该使用核心数据进行验证。
https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/ObjectValidation.html#//apple_ref/doc/uid/TP40001075-CH20-SW1
我不确定我的解决方案是否有问题,但对我来说制作代理对象太复杂了。我已经对其进行了测试,它似乎没有在启用 -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;
}
}
我对 coordinate
和 subtitle
以及任何其他导致问题的方法做了同样的事情。
显然有一些与主队列相关的“等待”操作,因为如果我将新创建的上下文的父级设置为“自身的上下文”,它就会死锁。
有趣的是,即使新创建的上下文在需要的线程上,如果没有 performBlockAndWait:
它也无法工作,即使我打印当前线程有或没有 performBlockAndWait:
它打印同样的东西。
如果对象的 projectName 属性 中有未保存的更改,将显示旧值,因为 persistentStoreCoordinator
用于新创建的上下文,但我可以控制它,不像我无法控制 Apple 在哪个线程上调用我的 MKAnnotation 方法。
我希望这对某人有用,即使我是在回答六年前提出的问题!
我正在使用我认为是 NSManagedObject
子类的相当典型的实现,它符合 MKAnnotation
协议,以便在 MKMapView
中显示。参见 setter 和 getters:
-(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 }];
块中,但这会导致线程锁定失败。
我应该忽略该错误并继续前进,还是对此有更好的做法?
我找不到原因。我验证了 NSManagedObject 在主 queue 上下文中。它被要求提供 queue 上的坐标,该坐标不是主 queue。我所做的修复是使用代理 object 作为传递给 MKMapview 的注释,而不是直接传递它。符合 MKAnnotation 协议的 NSObject class。使用我的 NSManagedObject 中的坐标和标题进行初始化,传递它而不是真正的交易。
首先,坐标应作为一个瞬态实现,包括一个神奇的 keyPathsForValuesAffectingCoordinate 方法。为简单起见,您可以从未缓存的瞬态开始,然后在必要时添加额外的代码来缓存它。
其次,应该使用核心数据进行验证。 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/ObjectValidation.html#//apple_ref/doc/uid/TP40001075-CH20-SW1
我不确定我的解决方案是否有问题,但对我来说制作代理对象太复杂了。我已经对其进行了测试,它似乎没有在启用 -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;
}
}
我对 coordinate
和 subtitle
以及任何其他导致问题的方法做了同样的事情。
显然有一些与主队列相关的“等待”操作,因为如果我将新创建的上下文的父级设置为“自身的上下文”,它就会死锁。
有趣的是,即使新创建的上下文在需要的线程上,如果没有 performBlockAndWait:
它也无法工作,即使我打印当前线程有或没有 performBlockAndWait:
它打印同样的东西。
如果对象的 projectName 属性 中有未保存的更改,将显示旧值,因为 persistentStoreCoordinator
用于新创建的上下文,但我可以控制它,不像我无法控制 Apple 在哪个线程上调用我的 MKAnnotation 方法。
我希望这对某人有用,即使我是在回答六年前提出的问题!