mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-09 10:20:59 +00:00
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:
1 parent
de1ebe6319
commit
d23bf7b927
3 files changed
+106
-113
No files matched your search
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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--)
|
||||
|
||||
Reference in new issue
Block a user