Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save tonyarnold/11081601 to your computer and use it in GitHub Desktop.
Save tonyarnold/11081601 to your computer and use it in GitHub Desktop.
#if DEBUG
/**
Toggles assertion of Core Data managed object contexts. Only available when DEBUG is defined.
When enabled, your application will throw an exception when accessing or modifying entities in a context other than their own.
*/
void TCBToggleAssertingContextQueues();
#endif
#import "NSManagedObjectContext+DebugSwizzling.h"
#if DEBUG
#import <CoreData/CoreData.h>
#import <objc/runtime.h>
#import "TCBRuntimeHelpers.h"
static NSUInteger TCBManagedObjectContextDisableChecksCount = 0;
@implementation NSManagedObjectContext (DebugSwizzling)
- (BOOL)tcb_shouldPerformContextQueueCheck
{
return (TCBManagedObjectContextDisableChecksCount == 0);
}
- (void)tcb_stopContextQueueCheck
{
TCBManagedObjectContextDisableChecksCount++;
}
- (void)tcb_startContextQueueCheck
{
TCBManagedObjectContextDisableChecksCount--;
}
- (id)tcb_dispatchQueue
{
id dispatchQueue = object_getIvar(self, class_getInstanceVariable([NSManagedObjectContext class], "_dispatchQueue"));
NSParameterAssert(dispatchQueue);
return dispatchQueue;
}
- (void)tcb_validateCurrentQueue
{
if (NO == [self tcb_shouldPerformContextQueueCheck]) {
return;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
NSAssert(dispatch_get_current_queue() == self.tcb_dispatchQueue, @"Accessing managed object from a different queue than its context uses: %@", self);
#pragma clang diagnostic pop
}
- (void)tcb_mergeChangesFromContextDidSaveNotification:(NSNotification *)notification
{
// Temporarily disable thread checks when merging changes from a did save notification — they are not always performed on the expected queue (although it's your responsibility to ensure that they are started on the correct queue for the context you are merging into!
[self tcb_stopContextQueueCheck];
[self tcb_mergeChangesFromContextDidSaveNotification:notification];
[self tcb_startContextQueueCheck];
}
@end
@implementation NSManagedObject (DebugSwizzling)
- (id)tcb_initWithEntity:(NSEntityDescription*)entity insertIntoManagedObjectContext:(NSManagedObjectContext*)context
{
[context tcb_validateCurrentQueue];
return [self tcb_initWithEntity:entity insertIntoManagedObjectContext:context];
}
- (void)tcb_willAccessValueForKey:(NSString*)key
{
[[self managedObjectContext] tcb_validateCurrentQueue];
[self tcb_willAccessValueForKey:key];
}
- (void)tcb_willChangeValueForKey:(NSString*)key
{
[[self managedObjectContext] tcb_validateCurrentQueue];
[self tcb_willChangeValueForKey:key];
}
- (void)tcb_willChangeValueForKey:(NSString*)inKey withSetMutation:(NSKeyValueSetMutationKind)inMutationKind usingObjects:(NSSet*)inObjects
{
[[self managedObjectContext] tcb_validateCurrentQueue];
[self tcb_willChangeValueForKey:inKey withSetMutation:inMutationKind usingObjects:inObjects];
}
@end
@implementation NSEntityDescription (DebugSwizzling)
+ (id)tcb_insertNewObjectForEntityForName:(NSString*)entityName inManagedObjectContext:(NSManagedObjectContext*)context
{
[context tcb_validateCurrentQueue];
return [self tcb_insertNewObjectForEntityForName:entityName inManagedObjectContext:context];
}
+ (NSEntityDescription*)tcb_entityForName:(NSString*)entityName inManagedObjectContext:(NSManagedObjectContext*)context
{
[context tcb_validateCurrentQueue];
return [self tcb_entityForName:entityName inManagedObjectContext:context];
}
@end
void TCBToggleAssertingContextQueues()
{
// NSManagedObjectContext
TCBSwapInstanceMethods([NSManagedObjectContext class], @selector(mergeChangesFromContextDidSaveNotification:), @selector(tcb_mergeChangesFromContextDidSaveNotification:));
// NSEntityDescription
TCBSwapClassMethods([NSEntityDescription class], @selector(insertNewObjectForEntityForName:inManagedObjectContext:), @selector(tcb_insertNewObjectForEntityForName:inManagedObjectContext:));
TCBSwapClassMethods([NSEntityDescription class], @selector(entityForName:inManagedObjectContext:), @selector(tcb_entityForName:inManagedObjectContext:));
// NSManagedObject
TCBSwapInstanceMethods([NSManagedObject class], @selector(initWithEntity:insertIntoManagedObjectContext:), @selector(tcb_initWithEntity:insertIntoManagedObjectContext:));
TCBSwapInstanceMethods([NSManagedObject class], @selector(willAccessValueForKey:), @selector(tcb_willAccessValueForKey:));
TCBSwapInstanceMethods([NSManagedObject class], @selector(willChangeValueForKey:), @selector(tcb_willChangeValueForKey:));
TCBSwapInstanceMethods([NSManagedObject class], @selector(willChangeValueForKey:withSetMutation:usingObjects:), @selector(tcb_willChangeValueForKey:withSetMutation:usingObjects:));
}
#endif /* if DEBUG */
//
// Created by Tony Arnold on 19/05/2014.
// Copyright (c) 2014 The CocoaBots. All rights reserved.
//
#import <Cocoa/Cocoa.h>
void TCBSwapInstanceMethods(Class cls, SEL originalSel, SEL newSel);
void TCBSwapClassMethods(Class cls, SEL originalSel, SEL newSel);
//
// Created by Tony Arnold on 19/05/2014.
// Copyright (c) 2014 The CocoaBots. All rights reserved.
//
#import "TCBRuntimeHelpers.h"
#import <objc/runtime.h>
void TCBSwapInstanceMethods(Class cls, SEL originalSel, SEL newSel)
{
Method originalMethod = class_getInstanceMethod(cls, originalSel);
Method newMethod = class_getInstanceMethod(cls, newSel);
NSCAssert(originalMethod != NULL, @"Method cannot be NULL");
NSCAssert(newMethod != NULL, @"Method cannot be NULL");
method_exchangeImplementations(originalMethod, newMethod);
}
void TCBSwapClassMethods(Class cls, SEL originalSel, SEL newSel)
{
Method originalMethod = class_getClassMethod(cls, originalSel);
Method newMethod = class_getClassMethod(cls, newSel);
NSCAssert(originalMethod != NULL, @"Method cannot be NULL");
NSCAssert(newMethod != NULL, @"Method cannot be NULL");
method_exchangeImplementations(originalMethod, newMethod);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment