2016-12-16 110 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這個標誌將仍然崩潰應用程序,即使它正確使用。 –