Created
April 14, 2014 11:52
-
-
Save jverkoey/10641155 to your computer and use it in GitHub Desktop.
Core Data Managed Object Context Debugging
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// NSManagedObjectContext+DebugSwizzling.h | |
#import <CoreData/CoreData.h> | |
#if DEBUG | |
/** | |
* Toggles debugging of Core Data managed object contexts. | |
* | |
* When enabled, will fire NSLogs in the following cases: | |
* | |
* - accessing/modifying an entity outside of a performBlock* operation. | |
* - accessing/modifying entities in a context other than their own. | |
*/ | |
void SwizzleNSManagedObjectContextForDebug(); | |
#else | |
#define SwizzleNSManagedObjectContextForDebug() | |
#endif | |
// NSManagedObjectContext+DebugSwizzling.m | |
#import "NSManagedObjectContext+DebugSwizzling.h" | |
#if DEBUG | |
static NSString* swizzle_NSManagedObjectContextActiveContextKey = @"swizzle_NSManagedObjectContextActiveContextKey"; | |
NSManagedObjectContext* GetManagedObjectContextForCurrentThread() { | |
NSMutableArray* stack = [NSThread currentThread].threadDictionary[swizzle_NSManagedObjectContextActiveContextKey]; | |
return [stack lastObject]; | |
} | |
void LogManagedObjectContextInfo(NSManagedObjectContext* activeMoc, NSString* entityName) { | |
NSManagedObjectContext* threadMoc = GetManagedObjectContextForCurrentThread(); | |
if (nil == threadMoc) { | |
NSLog(@"An entity (%@) is being used outside of a managed object context block!", entityName); | |
} else if (threadMoc != activeMoc) { | |
NSLog(@"An entity (%@) was being used in a different managed object context than the current thread's.", entityName); | |
NSLog(@"Thread context: %@", threadMoc); | |
NSLog(@"Active context: %@", activeMoc); | |
} | |
} | |
@implementation NSManagedObject (DebugSwizzling) | |
- (id)swizzle_initWithEntity:(NSEntityDescription *)entity insertIntoManagedObjectContext:(NSManagedObjectContext *)context { | |
LogManagedObjectContextInfo(self.managedObjectContext, NSStringFromClass(self.class)); | |
return [self swizzle_initWithEntity:entity insertIntoManagedObjectContext:context]; | |
} | |
- (void)swizzle_willAccessValueForKey:(NSString *)key { | |
LogManagedObjectContextInfo(self.managedObjectContext, NSStringFromClass(self.class)); | |
[self swizzle_willAccessValueForKey:key]; | |
} | |
- (void)swizzle_willChangeValueForKey:(NSString *)key { | |
LogManagedObjectContextInfo(self.managedObjectContext, NSStringFromClass(self.class)); | |
[self swizzle_willChangeValueForKey:key]; | |
} | |
@end | |
@implementation NSEntityDescription (DebugSwizzling) | |
+ (id)swizzle_insertNewObjectForEntityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context { | |
LogManagedObjectContextInfo(context, entityName); | |
return [self swizzle_insertNewObjectForEntityForName:entityName inManagedObjectContext:context]; | |
} | |
+ (NSEntityDescription *)swizzle_entityForName:(NSString *)entityName inManagedObjectContext:(NSManagedObjectContext *)context { | |
LogManagedObjectContextInfo(context, entityName); | |
return [self swizzle_entityForName:entityName inManagedObjectContext:context]; | |
} | |
@end | |
@implementation NSManagedObjectContext (DebugSwizzling) | |
- (void)debug_push { | |
NSMutableArray* stack = [NSThread currentThread].threadDictionary[swizzle_NSManagedObjectContextActiveContextKey]; | |
if (nil == stack) { | |
stack = [NSMutableArray array]; | |
[NSThread currentThread].threadDictionary[swizzle_NSManagedObjectContextActiveContextKey] = stack; | |
} | |
[stack addObject:self]; | |
} | |
- (void)debug_pop { | |
NSMutableArray* stack = [NSThread currentThread].threadDictionary[swizzle_NSManagedObjectContextActiveContextKey]; | |
[stack removeLastObject]; | |
} | |
- (void (^)())debug_blockWithBlock:(void (^)())block { | |
return ^{ | |
[self debug_push]; | |
if (block) { | |
block(); | |
} | |
[self debug_pop]; | |
}; | |
} | |
- (NSArray *)swizzle_executeFetchRequest:(NSFetchRequest *)request error:(NSError **)error { | |
[self debug_push]; | |
NSArray* results = [self swizzle_executeFetchRequest:request error:error]; | |
[self debug_pop]; | |
return results; | |
} | |
- (void)swizzle_performBlock:(void (^)())block { | |
[self swizzle_performBlock:[self debug_blockWithBlock:block]]; | |
} | |
- (void)swizzle_performBlockAndWait:(void (^)())block { | |
[self swizzle_performBlockAndWait:[self debug_blockWithBlock:block]]; | |
} | |
@end | |
void SwizzleNSManagedObjectContextForDebug() { | |
NISwapInstanceMethods([NSManagedObjectContext class], @selector(executeFetchRequest:error:), @selector(swizzle_executeFetchRequest:error:)); | |
NISwapInstanceMethods([NSManagedObjectContext class], @selector(performBlock:), @selector(swizzle_performBlock:)); | |
NISwapInstanceMethods([NSManagedObjectContext class], @selector(performBlockAndWait:), @selector(swizzle_performBlockAndWait:)); | |
NISwapClassMethods([NSEntityDescription class], @selector(insertNewObjectForEntityForName:inManagedObjectContext:), @selector(swizzle_insertNewObjectForEntityForName:inManagedObjectContext:)); | |
NISwapClassMethods([NSEntityDescription class], @selector(entityForName:inManagedObjectContext:), @selector(swizzle_entityForName:inManagedObjectContext:)); | |
NISwapInstanceMethods([NSManagedObject class], @selector(initWithEntity:insertIntoManagedObjectContext:), @selector(swizzle_initWithEntity:insertIntoManagedObjectContext:)); | |
NISwapInstanceMethods([NSManagedObject class], @selector(willAccessValueForKey:), @selector(swizzle_willAccessValueForKey:)); | |
NISwapInstanceMethods([NSManagedObject class], @selector(willChangeValueForKey:), @selector(swizzle_willChangeValueForKey:)); | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It'd probably be a good idea to cater for an entity not having an active context before you check anything else. Otherwise, super useful! Thanks @jverkoey!