Skip to content

Instantly share code, notes, and snippets.

@alloy
Last active December 22, 2015 09:48
Show Gist options
  • Save alloy/6453958 to your computer and use it in GitHub Desktop.
Save alloy/6453958 to your computer and use it in GitHub Desktop.
Prototype of an assertion that checks a NSManagedObject is being used on the queue belonging to its NSManagedObjectContext. This should obviously only be used during development.
/////////// Header
#import <CoreData/CoreData.h>
@interface NSManagedObject (FTCoreDataQueueContainmentAssertions)
+ (void)FTCDQCA_enableAssertions:(Class)klass;
@end
/////////// Implementation
#import <objc/runtime.h>
@implementation NSManagedObjectContext (FTCoreDataQueueContainmentAssertions)
// Problem is that using performBlockAndWait: can lead to deadlocks when used on the main queue.
//
// __block dispatch_queue_t queue = nil;
// [self performBlockAndWait:^{ queue = dispatch_get_current_queue(); }];
//
- (id)FTCDQCA_dispatchQueue;
{
id dispatchQueue = object_getIvar(self, class_getInstanceVariable([NSManagedObjectContext class], "_dispatchQueue"));
NSParameterAssert(dispatchQueue);
return dispatchQueue;
}
- (void)FTCDQCA_validateOnCorrectQueue;
{
NSAssert(dispatch_get_current_queue() == self.FTCDQCA_dispatchQueue, @"Accessing managed object from a different queue than its context uses: %@", self);
}
@end
@implementation NSManagedObject (FTCoreDataQueueContainmentAssertions)
+ (void)FTCDQCA_enableAssertions:(Class)klass;
{
Method FTCDQCA_willAccessValueForKey = class_getInstanceMethod(klass, @selector(FTCDQCA_willAccessValueForKey:));
class_addMethod(klass, @selector(willAccessValueForKey:), method_getImplementation(FTCDQCA_willAccessValueForKey), "v@:@");
Method FTCDQCA_willChangeValueForKey = class_getInstanceMethod(klass, @selector(FTCDQCA_willChangeValueForKey:));
class_addMethod(klass, @selector(willChangeValueForKey:), method_getImplementation(FTCDQCA_willChangeValueForKey), "v@:@");
}
- (void)FTCDQCA_willAccessValueForKey:(NSString *)key;
{
[self.managedObjectContext FTCDQCA_validateOnCorrectQueue];
[self FTCDQCA_willAccessValueForKey:key];
}
- (void)FTCDQCA_willChangeValueForKey:(NSString *)key;
{
[self.managedObjectContext FTCDQCA_validateOnCorrectQueue];
[super willChangeValueForKey:key];
}
@end
// NotYourEntity is a NSManagedObject subclass.
// Enable assertions:
[NSManagedObject FTCDQCA_enableAssertions:[NotYourEntity class]];
NotYourEntity *mainQueueEntity = (NotYourEntity *)[NSEntityDescription insertNewObjectForEntityForName:@"NotYourEntity"
inManagedObjectContext:self.managedObjectContext];
mainQueueEntity.anObjectValue = @"Correct";
NSManagedObjectContext *backgroundContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
backgroundContext.parentContext = self.managedObjectContext;
__block NotYourEntity *backgroundQueueEntity = nil;
[backgroundContext performBlockAndWait:^{
// This will hit the assertion.
mainQueueEntity.anObjectValue = @"YOLO";
backgroundQueueEntity = (NotYourEntity *)[NSEntityDescription insertNewObjectForEntityForName:@"NotYourEntity" inManagedObjectContext:backgroundContext];
backgroundQueueEntity.anObjectValue = @"Correct";
}];
// This will hit the assertion.
backgroundQueueEntity.anObjectValue = @"YOLO";
@alloy
Copy link
Author

alloy commented Sep 5, 2013

Also, dispatch_get_current_queue() is obviously deprecated, but as this is for development use only, it’s still good for now.

@JaviSoto
Copy link

JaviSoto commented Sep 5, 2013

One thing I haven’t figured out yet is a nice way to enable it for all NSManagedObject subclasses. I’ll look at this later, but if you know from experience, please leave a comment!

What do you mean? This would work for subclasses as long as they call super in the -willXXX methods.

@alloy
Copy link
Author

alloy commented Sep 6, 2013

What I mean is that I was not able to find a way to apply this to all NSManagedObject subclasses yet, possibly because iirc CD does some trickery with its subclasses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment