与许多iOS开发人员一样,我使用CoreData,就像许多使用CoreData的iOS开发人员一样,我有很难跟踪线程违规错误。我正在尝试实现一个调试策略,以便在CoreData并发规则被破坏时抛出异常。我的尝试在下面 - 我的问题是,这有效吗?它会产生误报吗?
摘要:创建NSManagedObject时,请记下该线程。每当稍后访问某个值时,检查当前线程是否与创建线程相同,如果没有则抛出异常。
#import "NSManagedObject+DebugTracking.h"
#import "NSObject+DTRuntime.h"
#import <objc/runtime.h>
#import "NSManagedObjectContext+DebugThreadTracking.h"
@implementation NSManagedObject (DebugTracking)
+(void)load {
[NSManagedObject swizzleMethod:@selector(willAccessValueForKey:) withMethod:@selector(swizzled_willAccessValueForKey:)];
[NSManagedObject swizzleMethod:@selector(initWithEntity:insertIntoManagedObjectContext:) withMethod:@selector(swizzled_initWithEntity:insertIntoManagedObjectContext:)];
}
- (__kindof NSManagedObject *)swizzled_initWithEntity:(NSEntityDescription *)entity
insertIntoManagedObjectContext:(NSManagedObjectContext *)context
{
NSManagedObject *object = [self swizzled_initWithEntity:entity insertIntoManagedObjectContext:context];
NSLog(@"Initialising an object of type: %@", NSStringFromClass([self class]));
object.debugThread = [NSThread currentThread];
return object;
}
-(void)swizzled_willAccessValueForKey:(NSString *)key {
NSThread *thread = self.debugThread;
if (!thread) {
NSLog(@"No Thread set");
} else if (thread != [NSThread currentThread]) {
[NSException raise:@"CoreData thread violation exception" format:@"Property accessed from a different thread than the object's creation thread. Type: %@", NSStringFromClass([self class])];
} else {
NSLog(@"All is well");
}
[self swizzled_willAccessValueForKey: key];
}
-(NSThread *)debugThread {
return objc_getAssociatedObject(self, @selector(debugThread));
}
-(void)setDebugThread:(NSThread *)debugThread {
objc_setAssociatedObject(self, @selector(debugThread), debugThread, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
答案 0 :(得分:9)
不,那不是个好主意。您将部分复制Apple构建到框架中的内容会遇到相当大的麻烦。
如果编辑目标的方案,可以将-com.apple.CoreData.ConcurrencyDebug 1
添加到启动时传递的参数:
完成后,所有并发冲突都会导致立即崩溃。你会知道这是一个并发冲突,因为堆栈跟踪中的“留给我们的一切都是荣誉”消息:
您将看到导致并发冲突的确切代码行。
您可能还想添加其他几个核心数据调试参数,com.apple.CoreData.SQLDebug
和com.apple.CoreData.Logging.stderr
。它们不会改变并发调试,但它们会使Xcode打印出一条阅读CoreData: annotation: Core Data multi-threading assertions enabled
的消息,以便您知道它已经开启。
答案 1 :(得分:1)
不,它无效。您的代码假设上下文始终在同一个线程上执行块。但事实上它总是在同一个队列上运行,队列和线程不是一回事。
例如,尝试使用performBlockAndWait:来检查它是否使用相同的线程performBlock:运行。 FWIK performBlockAndWait:使用调用它的线程。
答案 2 :(得分:0)
您可以添加一个调试标志,该标志显示有关Core Data执行的操作的大量信息,包括但不限于底层SQL操作。见this answer。请注意,您可以将调试级别从1更改为2或3以获得更多调试信息。
我不确定它是否会对您的具体情况有所帮助,但不知道确切的问题和背景,但它应该有所帮助。此外,您还可以检查堆栈跟踪,这对于Xcode 7和8中的多线程调试有很大改进。