这是调试CoreData并发问题的有效方法吗?

时间:2016-12-16 01:47:42

标签: ios objective-c core-data

与许多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

3 个答案:

答案 0 :(得分:9)

不,那不是个好主意。您将部分复制Apple构建到框架中的内容会遇到相当大的麻烦。

如果编辑目标的方案,可以将-com.apple.CoreData.ConcurrencyDebug 1添加到启动时传递的参数:

Concurrency debug flag

完成后,所有并发冲突都会导致立即崩溃。你会知道这是一个并发冲突,因为堆栈跟踪中的“留给我们的一切都是荣誉”消息:

all that is left to us is honor

您将看到导致并发冲突的确切代码行。

您可能还想添加其他几个核心数据调试参数,com.apple.CoreData.SQLDebugcom.apple.CoreData.Logging.stderr。它们不会改变并发调试,但它们会使Xcode打印出一条阅读CoreData: annotation: Core Data multi-threading assertions enabled的消息,以便您知道它已经开启。

extra debug flags

答案 1 :(得分:1)

不,它无效。您的代码假设上下文始终在同一个线程上执行块。但事实上它总是在同一个队列上运行,队列和线程不是一回事。

例如,尝试使用performBlockAndWait:来检查它是否使用相同的线程performBlock:运行。 FWIK performBlockAndWait:使用调用它的线程。

答案 2 :(得分:0)

您可以添加一个调试标志,该标志显示有关Core Data执行的操作的大量信息,包括但不限于底层SQL操作。见this answer。请注意,您可以将调试级别从1更改为2或3以获得更多调试信息。

我不确定它是否会对您的具体情况有所帮助,但不知道确切的问题和背景,但它应该有所帮助。此外,您还可以检查堆栈跟踪,这对于Xcode 7和8中的多线程调试有很大改进。