// // NGAAutoObserver.m // P4Menu // // Created by Michael Bishop on 10/3/11. // Copyright 2011 __MyCompanyName__. All rights reserved. // #import "NGAAutoObserver.h" #import "NGAUtilities.h" #import NSString * const kObserverMethodPrefix = @"valueDidChangeFor"; static NSString * keyPathFromSelectorName(NSString * selectorName) { NSScanner * scanner = [NSScanner scannerWithString:selectorName]; if (! [scanner scanString:kObserverMethodPrefix intoString:nil] ) return nil; NSString * keyPath = [[[selectorName substringFromIndex:[scanner scanLocation]] NGA_stringByConvertingFirstWordToLowerCase] stringByReplacingOccurrencesOfString:@"_" withString:@"."]; if ( [keyPath hasSuffix:@":"] ) keyPath = [keyPath substringToIndex:[keyPath length] - 1]; return keyPath; } static NSString * selectorNameFromKeyPath(NSString * keyPath) { NSString * selectorName = [kObserverMethodPrefix stringByAppendingString:[[keyPath NGA_stringByConvertingFirstCharacterToUpperCase] stringByReplacingOccurrencesOfString:@"." withString:@"_"] ]; return selectorName; } @interface NGAAutoObserver : NSObject { } +(NGAAutoObserver*)sharedAutoObserver; -(void)registerObserverMethodsForObject:(id)object; -(void)deregisterObserverMethodsForObject:(id)object; @end @implementation NGAAutoObserver SINGLETON_IMPLEMENTATION(NGAAutoObserver, sharedAutoObserver) - (id)init { self = [super init]; if (self) { } return self; } DEALLOC ( ) //-(void)enumeratePropertiesOfClass:(Class)class usingBlock:(void(^)(NSString * propertyName))block //{ // unsigned int propertyCount = 0; // objc_property_t * propertyList = class_copyPropertyList(class, &propertyCount); // objc_property_t * currentProperty = propertyList; // while (propertyCount > 0) // { // const char * propertyNameCString = property_getName(*currentProperty); // NSString * propertyName = [NSString stringWithUTF8String:propertyNameCString]; // block( propertyName ); // currentProperty++; // propertyCount--; // } // // free( propertyList ); //} -(void)registerObserverMethodsOnObject:(id)object forKeyPaths:(NSArray*)keyPaths { for (NSString * keyPath in keyPaths) { [object addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; } } -(void)deregisterObserverMethodsOnObject:(id)object forKeyPaths:(NSArray*)keyPaths { for (NSString * keyPath in keyPaths) { [object removeObserver:self forKeyPath:keyPath]; } } -(void)enumerateMethodsOfClass:(Class)class usingBlock:(void(^)(SEL selector))block { unsigned int methodCount = 0; Method * methodList = class_copyMethodList(class, &methodCount); Method * currentMethod = methodList; while (methodCount > 0) { SEL selector = method_getName(*currentMethod); block( selector ); currentMethod++; methodCount--; } free( methodList ); } -(void)registerObserverMethodsForObject:(id)object { [self enumerateMethodsOfClass:[object class] usingBlock:^(SEL selector) { NSString * keyPath = keyPathFromSelectorName(NSStringFromSelector(selector)); if ( !keyPath ) return; [object addObserver:self forKeyPath:keyPath options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:NULL]; }]; } -(void)deregisterObserverMethodsForObject:(id)object { [self enumerateMethodsOfClass:[object class] usingBlock:^(SEL selector) { NSString * keyPath = keyPathFromSelectorName(NSStringFromSelector(selector)); if ( !keyPath ) return; [object removeObserver:self forKeyPath:keyPath]; }]; } -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { NSString * selectorName = selectorNameFromKeyPath(keyPath); if ([object respondsToSelector:NSSelectorFromString(selectorName)]) [[NSInvocation invocationWithSelectorName:selectorName target:object] invoke]; selectorName = [selectorName stringByAppendingString:@":"]; if ([object respondsToSelector:NSSelectorFromString(selectorName)]) { NSInvocation * invocation = [NSInvocation invocationWithSelectorName:selectorName target:object]; [invocation setArgument:&change atIndex:2]; [invocation invoke]; } } @end @implementation NSObject (NGAAutoObservation) -(void)registerObserverMethodsForKeyPaths:(NSArray*)keyPaths { [[NGAAutoObserver sharedAutoObserver] registerObserverMethodsOnObject:self forKeyPaths:keyPaths]; } -(void)deregisterObserverMethodsForKeyPaths:(NSArray*)keyPaths { [[NGAAutoObserver sharedAutoObserver] deregisterObserverMethodsOnObject:self forKeyPaths:keyPaths]; } -(void)registerAllAutoObservationMethods { [[NGAAutoObserver sharedAutoObserver] registerObserverMethodsForObject:self]; } -(void)unregisterAllAutoObservationMethods { [[NGAAutoObserver sharedAutoObserver] deregisterObserverMethodsForObject:self]; } @end