Some KVC cleanup:

1. Added class_getInstanceVariable to runtime.
2. Changed KVC to rely on class-defined variables vs prototypal ones.
3. Modified KVO code to use public APIs.

Reviewed by me.
This commit is contained in:
Francisco Ryan Tolmasky I committed 2010-12-23 23:46:23 -08:00
1 parent de1ebe6319
commit d23bf7b927
3 files changed
+106 -113

No files matched your search

+62 -80
View File
@@ -26,114 +26,91 @@
@import "CPObject.j"
var CPObjectAccessorsForClass = nil,
CPObjectModifiersForClass = nil;
CPUndefinedKeyException = @"CPUndefinedKeyException";
CPTargetObjectUserInfoKey = @"CPTargetObjectUserInfoKey";
CPUnknownUserInfoKey = @"CPUnknownUserInfoKey";
var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
CPObjectModifiersForClassKey = @"$CPObjectModifiersForClassKey";
var CPObjectAccessorsForClassKey = @"$CPObjectAccessorsForClassKey",
CPObjectModifiersForClassKey = @"$CPObjectModifiersForClassKey",
CPObjectInstanceVariablesForClassKey = @"$CPObjectInstanceVariablesForClassKey";
var Null = [CPNull null];
var _accessorForKey = function(theClass, aKey)
{
var selector = nil,
accessors = theClass[CPObjectAccessorsForClassKey];
if (accessors)
{
selector = accessors[aKey];
if (!accessors)
accessors = theClass[CPObjectAccessorsForClassKey] = { };
if (selector)
return selector === Null ? nil : selector;
}
else
accessors = theClass[CPObjectAccessorsForClassKey] = {};
else if (accessors.hasOwnProperty(aKey))
return accessors[aKey];
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substr(1);
if ([theClass instancesRespondToSelector:selector = CPSelectorFromString("get" + capitalizedKey)] ||
[theClass instancesRespondToSelector:selector = CPSelectorFromString(aKey)] ||
[theClass instancesRespondToSelector:selector = CPSelectorFromString("is" + capitalizedKey)] ||
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_get" + capitalizedKey)] || //FIXME: is deprecated in Cocoa 10.3
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_" + aKey)] || //FIXME: is deprecated in Cocoa 10.3
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_is" + capitalizedKey)]) //FIXME: was NEVER supported by Cocoa
{
accessors[aKey] = selector;
[theClass instancesRespondToSelector:selector = CPSelectorFromString("get" + capitalizedKey)] ||
[theClass instancesRespondToSelector:selector = CPSelectorFromString(aKey)] ||
[theClass instancesRespondToSelector:selector = CPSelectorFromString("is" + capitalizedKey)] ||
//FIXME: is deprecated in Cocoa 10.3
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_get" + capitalizedKey)] ||
//FIXME: is deprecated in Cocoa 10.3
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_" + aKey)] ||
//FIXME: was NEVER supported by Cocoa
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_is" + capitalizedKey)] ||
(selector = nil);
return selector;
}
accessors[aKey] = selector;
accessors[aKey] = Null;
return nil;
return selector;
}
var _modifierForKey = function(theClass, aKey)
{
if (!CPObjectModifiersForClass)
CPObjectModifiersForClass = [CPDictionary dictionary];
var selector = nil,
modifiers = theClass[CPObjectModifiersForClassKey];
var UID = [theClass UID],
selector = nil,
modifiers = [CPObjectModifiersForClass objectForKey:UID];
if (!modifiers)
modifiers = theClass[CPObjectModifiersForClassKey] = { };
if (modifiers)
{
selector = [modifiers objectForKey:aKey];
if (selector)
return selector === Null ? nil : selector;
}
else
{
modifiers = [CPDictionary dictionary];
[CPObjectModifiersForClass setObject:modifiers forKey:UID];
}
else if (modifiers.hasOwnProperty(aKey))
return modifiers[aKey];
var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substr(1) + ':';
if ([theClass instancesRespondToSelector:selector = CPSelectorFromString("set" + capitalizedKey)] ||
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_set" + capitalizedKey)]) //FIXME: deprecated in Cocoa 10.3
{
[modifiers setObject:selector forKey:aKey];
[theClass instancesRespondToSelector:selector = CPSelectorFromString("set" + capitalizedKey)] ||
//FIXME: deprecated in Cocoa 10.3
[theClass instancesRespondToSelector:selector = CPSelectorFromString("_set" + capitalizedKey)] ||
(selector = nil);
return selector;
}
modifiers[aKey] = selector;
[modifiers setObject:Null forKey:aKey];
return nil;
return selector;
}
var _ivarForKey = function(theObject, aKey)
var _ivarForKey = function(theClass, aKey)
{
var ivar = '_' + aKey;
var variables = theClass[CPObjectInstanceVariablesForClassKey];
if (typeof theObject[ivar] != "undefined")
return ivar;
if (!variables)
variables = theClass[CPObjectInstanceVariablesForClassKey] = { };
var isKey = "is" + aKey.charAt(0).toUpperCase() + aKey.substr(1);
else if (variables.hasOwnProperty(aKey))
return variables[aKey];
ivar = '_' + isKey;
var name = '_' + aKey;
if (typeof theObject[ivar] != "undefined")
return ivar;
if (!class_getInstanceVariable(theClass, name))
{
var isKey = "is" + aKey.charAt(0).toUpperCase() + aKey.substr(1);
ivar = aKey;
class_getInstanceVariable(theClass, name = '_' + isKey) ||
class_getInstanceVariable(theClass, name = aKey) ||
class_getInstanceVariable(theClass, name = isKey) ||
(name = nil);
}
if (typeof theObject[ivar] != "undefined")
return ivar;
variables[aKey] = name;
ivar = isKey;
if (typeof theObject[ivar] != "undefined")
return ivar;
return nil;
return name;
}
@implementation CPObject (CPKeyValueCoding)
@@ -157,7 +134,7 @@ var _ivarForKey = function(theObject, aKey)
if ([theClass accessInstanceVariablesDirectly])
{
var ivar = _ivarForKey(self, aKey);
var ivar = _ivarForKey(theClass, aKey);
if (ivar)
return self[ivar];
@@ -170,7 +147,7 @@ var _ivarForKey = function(theObject, aKey)
{
var firstDotIndex = aKeyPath.indexOf(".");
if (firstDotIndex === -1)
if (firstDotIndex === CPNotFound)
return [self valueForKey:aKeyPath];
var firstKeyComponent = aKeyPath.substring(0, firstDotIndex),
@@ -193,6 +170,7 @@ var _ivarForKey = function(theObject, aKey)
if (value === nil)
[dictionary setObject:[CPNull null] forKey:key];
else
[dictionary setObject:value forKey:key];
}
@@ -209,11 +187,12 @@ var _ivarForKey = function(theObject, aKey)
- (void)setValue:(id)aValue forKeyPath:(CPString)aKeyPath
{
if (!aKeyPath) aKeyPath = @"self";
if (!aKeyPath)
aKeyPath = @"self";
var firstDotIndex = aKeyPath.indexOf(".");
if (firstDotIndex === -1)
if (firstDotIndex === CPNotFound)
return [self setValue:aValue forKey:aKeyPath];
var firstKeyComponent = aKeyPath.substring(0, firstDotIndex),
@@ -233,7 +212,7 @@ var _ivarForKey = function(theObject, aKey)
if ([theClass accessInstanceVariablesDirectly])
{
var ivar = _ivarForKey(self, aKey);
var ivar = _ivarForKey(theClass, aKey);
if (ivar)
{
@@ -259,8 +238,10 @@ var _ivarForKey = function(theObject, aKey)
while (key = [keyEnumerator nextObject])
{
value = [keyedValues objectForKey: key];
if (value === [CPNull null])
[self setValue: nil forKey: key];
else
[self setValue: value forKey: key];
}
@@ -275,7 +256,7 @@ var _ivarForKey = function(theObject, aKey)
@end
@implementation CPDictionary (KeyValueCoding)
@implementation CPDictionary (CPKeyValueCoding)
- (id)valueForKey:(CPString)aKey
{
@@ -287,15 +268,16 @@ var _ivarForKey = function(theObject, aKey)
- (void)setValue:(id)aValue forKey:(CPString)aKey
{
if (aValue)
if (aValue !== nil)
[self setObject:aValue forKey:aKey];
else
[self removeObjectForKey: aKey];
[self removeObjectForKey:aKey];
}
@end
@implementation CPNull (KeyValueCoding)
@implementation CPNull (CPKeyValueCoding)
- (id)valueForKey:(CPString)aKey
{
+8 -14
View File
@@ -37,19 +37,19 @@
{
}
- (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)key
- (void)willChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
{
}
- (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)key
- (void)didChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
{
}
- (void)willChangeValueForKey:(CPString)key withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
- (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
{
}
- (void)didChangeValueForKey:(CPString)key withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
- (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
{
}
@@ -282,18 +282,12 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld,
kvoClass._replacedKeys = _replacedKeys;
//copy in the methods from our model subclass
var methodList = _CPKVOModelSubclass.method_list;
var methods = class_copyMethodList(_CPKVOModelSubclass);
if ([_targetObject isKindOfClass:[CPDictionary class]])
methodList = methodList.concat(_CPKVOModelDictionarySubclass.method_list);
methods = methods.concat(class_copyMethodList(_CPKVOModelDictionarySubclass));
var count = methodList.length,
i = 0;
for (; i < count; i++)
{
var method = methodList[i];
class_addMethod(kvoClass, method_getName(method), method_getImplementation(method), "");
}
class_addMethods(kvoClass, methods);
_targetObject.isa = kvoClass;
}
+36 -19
View File
@@ -61,11 +61,12 @@ GLOBAL(objj_class) = function(displayName)
this.name = NULL;
this.info = 0;
this.ivars = [];
this.ivar_list = [];
this.ivar_store = function() { };
this.ivar_dtable = this.ivar_store.prototype;
this.method_list = [];
this.method_hash = {};
this.method_store = function() { };
this.method_dtable = this.method_store.prototype;
@@ -135,10 +136,15 @@ GLOBAL(class_addIvar) = function(/*Class*/ aClass, /*String*/ aName, /*String*/
{
var thePrototype = aClass.allocator.prototype;
// FIXME: Use getInstanceVariable
if (typeof thePrototype[aName] != "undefined")
return NO;
aClass.ivars.push(new objj_ivar(aName, aType));
var ivar = new objj_ivar(aName, aType);
aClass.ivar_list.push(ivar);
aClass.ivar_dtable[aName] = ivar;
thePrototype[aName] = NULL;
return YES;
@@ -157,9 +163,12 @@ GLOBAL(class_addIvars) = function(/*Class*/ aClass, /*Array*/ivars)
var ivar = ivars[index],
name = ivar.name;
// FIXME: Use getInstanceVariable
if (typeof thePrototype[name] === "undefined")
{
aClass.ivars.push(ivar);
aClass.ivar_list.push(ivar);
aClass.ivar_dtable[name] = ivar;
thePrototype[name] = NULL;
}
}
@@ -169,20 +178,18 @@ DISPLAY_NAME(class_addIvars);
GLOBAL(class_copyIvarList) = function(/*Class*/ aClass)
{
return aClass.ivars.slice(0);
return aClass.ivar_list.slice(0);
}
DISPLAY_NAME(class_copyIvarList);
//#define class_copyIvarList(aClass) (aClass.ivars.slice(0))
//#define class_copyIvarList(aClass) (aClass.ivar_list.slice(0))
#define METHOD_DISPLAY_NAME(aClass, aMethod) (ISMETA(aClass) ? '+' : '-') + " [" + class_getName(aClass) + ' ' + method_getName(aMethod) + ']'
GLOBAL(class_addMethod) = function(/*Class*/ aClass, /*SEL*/ aName, /*IMP*/ anImplementation, /*Array<String>*/ types)
{
if (aClass.method_hash[aName])
return NO;
// FIXME: return NO if it exists?
var method = new objj_method(aName, anImplementation, types);
aClass.method_list.push(method);
@@ -215,9 +222,7 @@ GLOBAL(class_addMethods) = function(/*Class*/ aClass, /*Array*/ methods)
{
var method = methods[index];
if (aClass.method_hash[method.name])
continue;
// FIXME: Don't do it if it exists?
method_list.push(method);
method_dtable[method.name] = method;
@@ -246,6 +251,19 @@ GLOBAL(class_getInstanceMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector)
DISPLAY_NAME(class_getInstanceMethod);
GLOBAL(class_getInstanceVariable) = function(/*Class*/ aClass, /*String*/ aName)
{
if (!aClass || !aName)
return NULL;
// FIXME: this doesn't appropriately deal with Object's properties.
var variable = aClass.ivar_dtable[aName];
return variable;
}
DISPLAY_NAME(class_getInstanceVariable);
GLOBAL(class_getClassMethod) = function(/*Class*/ aClass, /*SEL*/ aSelector)
{
if (!aClass || !aSelector)
@@ -370,12 +388,11 @@ GLOBAL(objj_allocateClassPair) = function(/*Class*/ superclass, /*String*/ aName
// Give our current allocator all the instance variables of our super class' allocator.
classObject.allocator.prototype = new superclass.allocator;
// "Inheret" parent methods.
classObject.method_store.prototype = new superclass.method_store;
classObject.method_dtable = classObject.method_store.prototype;
// "Inheret" parent properties.
classObject.ivar_dtable = classObject.ivar_store.prototype = new superclass.ivar_store;
classObject.method_dtable = classObject.method_store.prototype = new superclass.method_store;
metaClassObject.method_store.prototype = new superclass.isa.method_store;
metaClassObject.method_dtable = metaClassObject.method_store.prototype;
metaClassObject.method_dtable = metaClassObject.method_store.prototype = new superclass.isa.method_store;
// Set up the actual class hierarchy.
classObject.super_class = superclass;
@@ -456,7 +473,7 @@ class_createInstance = function(/*Class*/ aClass)
while (theClass)
{
var ivars = theClass.ivars,
var ivars = theClass.ivar_list,
count = ivars.length;
while (count--)