From ce04c9705322088bb565824728b0dc0cb66fa1ad Mon Sep 17 00:00:00 2001 From: Ross Boucher Date: Sun, 21 Sep 2008 14:57:06 -0700 Subject: [PATCH] Initial KVO implementation, not yet working. --- Foundation/CPKeyValueCoding.j | 10 +- Foundation/CPKeyValueObserving.j | 227 ++++++++++++++++++++++++++++++- Foundation/CPObject.j | 2 +- 3 files changed, 232 insertions(+), 7 deletions(-) diff --git a/Foundation/CPKeyValueCoding.j b/Foundation/CPKeyValueCoding.j index d825acabd..a064589fc 100644 --- a/Foundation/CPKeyValueCoding.j +++ b/Foundation/CPKeyValueCoding.j @@ -152,12 +152,13 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; - (id)valueForKey:(CPString)aKey { - var selector = [isa _accessorForKey:aKey]; + var theClass = [self class], + selector = [theClass _accessorForKey:aKey]; if (selector) return objj_msgSend(self, selector); - if([isa accessInstanceVariablesDirectly]) + if([theClass accessInstanceVariablesDirectly]) { var ivar = [self _ivarForKey:aKey]; @@ -227,12 +228,13 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; - (void)setValue:(id)aValue forKey:(CPString)aKey { - var selector = [isa _modifierForKey:aKey]; + var theClass = [self class], + selector = [theClass _modifierForKey:aKey]; if (selector) return objj_msgSend(self, selector, aValue); - if([isa accessInstanceVariablesDirectly]) + if([theClass accessInstanceVariablesDirectly]) { var ivar = [self _ivarForKey:aKey]; diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j index ea1dc2818..6b16584bc 100644 --- a/Foundation/CPKeyValueObserving.j +++ b/Foundation/CPKeyValueObserving.j @@ -20,9 +20,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +import "CPArray.j" +import "CPDictionary.j" import "CPObject.j" - @implementation CPObject (KeyValueObserving) - (void)willChangeValueForKey:(CPString)aKey @@ -36,4 +37,226 @@ import "CPObject.j" } -@end \ No newline at end of file +- addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(unsigned)options context:(id)aContext +{ + if (!anObserver || !aPath) + return; + + [[_CPKVOProxy proxyForObject:self] _addObserver:anObserver forKeyPath:aPath options:options context:aContext]; +} + +- removeObserver:(id)anObserver forKeyPath:(CPString)aPath +{ + if (!anObserver || !aPath) + return; + + [[KVOProxyMap objectForKey:[self hash]] _removeObserver:anObserver forKeyPath:aPath]; +} + +- (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey +{ + return YES; +} + +@end + +var KVOProxyMap = [CPDictionary dictionary]; + +//rule of thumb: _ methods are called on the real proxy object, others are called on the "fake" proxy object (aka the real object) + +@implementation _CPKVOProxy : CPObject +{ + id _targetObject; + Class _nativeClass; + CPDictionary _observerInfos; + CPDictionary _replacementMethods; +} + ++ (id)proxyForObject:(CPObject)anObject +{ + var proxy = [KVOProxyMap objectForKey:[anObject hash]]; + + if (proxy) + return proxy; + + proxy = [[self alloc] initWithTarget:anObject]; + + [proxy _replaceSetters]; + + anObject.isa = proxy.isa; + + [KVOProxyMap setObject:proxy forKey:[anObject hash]]; + + return proxy; +} + +- (id)initWithTarget:(id)aTarget +{ + self = [super init]; + + _targetObject = aTarget; + _nativeClass = [aTarget class]; + _observerInfos = [CPDictionary dictionary]; + _replacementMethods = [CPDictionary dictionary]; + + return self; +} + +- (void)_replaceSetters +{ + var currentClass = [_targetObject class]; + + while (currentClass && currentClass != currentClass.super_class) + { + var methodList = currentClass.method_list, + count = methodList.length; + + for (var i=0; i 2 || !([selector hasPrefix:@"set"] || [selector hasPrefix:@"_set"])) + return nil; + + var keyIndex = selector.indexOf("set") + "set".length, + colonIndex = selector.indexOf(":"); + + return selector.charAt(keyIndex).toLowerCase() + selector.substring(keyIndex+1, colonIndex); +} diff --git a/Foundation/CPObject.j b/Foundation/CPObject.j index e4f633d66..7bae52908 100644 --- a/Foundation/CPObject.j +++ b/Foundation/CPObject.j @@ -267,7 +267,7 @@ */ - (void)forward:(SEL)aSelector :(marg_list)args { - var signature = [self methodSignatureForSelector:_cmd]; + var signature = [self methodSignatureForSelector:aSelector]; if (signature) {