2016-12-16 20 views
3

与许多iOS开发人员一样,我使用CoreData,并且像许多使用CoreData的iOS开发人员一样,我有难以追踪的线程违规错误。我试图在CoreData并发规则被破坏时实现一个抛出异常的调试策略。我的尝试在下面 - 我的问题是,这是否有效?它会产生误报吗?这是调试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

您可以添加,显示了很多有关核心数据,包括执行的操作信息的调试标志,但不限于,下面的SQL操作。见this answer。请注意,您可以将调试级别从1更改为2或3以获取更多调试信息。

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

1

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

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

+0

我会假设'performBlockAndWait:''使用dispatch_sync',其中,根据经验,_often_有你提到的,但可能不依赖于你的操作系统版本,设备,等等,等等。但是,不管行为。你是完全正确的:作者有不恰当的线程和队列。 – Tommy

+0

绝对正确 –

8

不,这不是一个好主意。将部分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

+0

我发现这是非常不可靠的 - 通常它只会在某些尝试将对象保存到存储区时引发错误,但如果来自错误队列的某些内容尝试读取NSManagedObject的内容时不会抛出错误属性。 –

+1

这种技术在过去对我有很好的效果。 –

+1

我注意到,如果你使用NSAsynchronousFetchRequest这个标志将仍然崩溃应用程序,即使它正确使用。 –

相关问题