mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-04 08:06:16 +00:00
Fix for incorrectly installing KVO if all the mutation methods aren't implemented.
Reviewed by me.
This commit is contained in:
1 parent
f64cc4c655
commit
6ff21692f1
1 file changed
+289
-149
+289
-149
@@ -299,39 +299,301 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew | CPKeyValueObservingOptionOld,
|
||||
if ([_replacedKeys containsObject:aKey] || ![_nativeClass automaticallyNotifiesObserversForKey:aKey])
|
||||
return;
|
||||
|
||||
var currentClass = _nativeClass,
|
||||
capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
|
||||
found = false,
|
||||
replacementMethods = [
|
||||
"set" + capitalizedKey + ":", _kvoMethodForMethod,
|
||||
"_set" + capitalizedKey + ":", _kvoMethodForMethod,
|
||||
"insertObject:in" + capitalizedKey + "AtIndex:", _kvoInsertMethodForMethod,
|
||||
"insert" + capitalizedKey + ":atIndexes:", _kvoInsertManyMethodForMethod,
|
||||
"replaceObjectIn" + capitalizedKey + "AtIndex:withObject:", _kvoReplaceMethodForMethod,
|
||||
"replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":", _kvoReplaceManyMethodForMethod,
|
||||
"removeObjectFrom" + capitalizedKey + "AtIndex:", _kvoRemoveMethodForMethod,
|
||||
"remove" + capitalizedKey + "AtIndexes:", _kvoRemoveManyMethodForMethod,
|
||||
"add" + capitalizedKey + "Object:", _kvoUnionMethodForMethod,
|
||||
"add" + capitalizedKey + ":", _kvoUnionManyMethodForMethod,
|
||||
"remove" + capitalizedKey + "Object:", _kvoMinusMethodForMethod,
|
||||
"remove" + capitalizedKey + ":", _kvoMinusManyMethodForMethod,
|
||||
"intersect" + capitalizedKey + ":", _kvoIntersectManyMethodForMethod
|
||||
];
|
||||
var theClass = _nativeClass,
|
||||
KVOClass = _targetObject.isa,
|
||||
capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1);
|
||||
|
||||
var i = 0,
|
||||
count = replacementMethods.length;
|
||||
// Attribute and To-One Relationships
|
||||
var setKey_selector = sel_getUid("set" + capitalizedKey + ":"),
|
||||
setKey_method = class_getInstanceMethod(theClass, setKey_selector);
|
||||
|
||||
for (; i < count; i += 2)
|
||||
if (setKey_method)
|
||||
{
|
||||
var theSelector = sel_getName(replacementMethods[i]),
|
||||
theReplacementMethod = replacementMethods[i + 1];
|
||||
var setKey_method_imp = setKey_method.method_imp;
|
||||
|
||||
if ([_nativeClass instancesRespondToSelector:theSelector])
|
||||
class_addMethod(KVOClass, setKey_selector, function(self, _cmd, anObject)
|
||||
{
|
||||
var theMethod = class_getInstanceMethod(_nativeClass, theSelector);
|
||||
[self willChangeValueForKey:aKey];
|
||||
|
||||
class_addMethod(_targetObject.isa, theSelector, theReplacementMethod(aKey, theMethod), "");
|
||||
[_replacedKeys addObject:aKey];
|
||||
setKey_method_imp(self, _cmd, anObject);
|
||||
|
||||
[self didChangeValueForKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
// FIXME: Deprecated.
|
||||
var _setKey_selector = sel_getUid("_set" + capitalizedKey + ":"),
|
||||
_setKey_method = class_getInstanceMethod(theClass, _setKey_selector);
|
||||
|
||||
if (_setKey_method)
|
||||
{
|
||||
var _setKey_method_imp = _setKey_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, _setKey_selector, function(self, _cmd, anObject)
|
||||
{
|
||||
[self willChangeValueForKey:aKey];
|
||||
|
||||
_setKey_method_imp(self, _cmd, anObject);
|
||||
|
||||
[self didChangeValueForKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
// Ordered To-Many Relationships
|
||||
var insertObject_inKeyAtIndex_selector = sel_getUid("insertObject:in" + capitalizedKey + "AtIndex:"),
|
||||
insertObject_inKeyAtIndex_method =
|
||||
class_getInstanceMethod(theClass, insertObject_inKeyAtIndex_selector),
|
||||
|
||||
insertKey_atIndexes_selector = sel_getUid("insert" + capitalizedKey + ":atIndex:"),
|
||||
insertKey_atIndexes_method =
|
||||
class_getInstanceMethod(theClass, insertKey_atIndexes_selector),
|
||||
|
||||
removeObjectFromKeyAtIndex_selector = sel_getUid("removeObjectFrom" + capitalizedKey + "AtIndex:"),
|
||||
removeObjectFromKeyAtIndex_method =
|
||||
class_getInstanceMethod(theClass, removeObjectFromKeyAtIndex_selector),
|
||||
|
||||
removeKeyAtIndexes_selector = sel_getUid("remove" + capitalizedKey + "AtIndexes:"),
|
||||
removeKeyAtIndexes_method = class_getInstanceMethod(theClass, removeKeyAtIndexes_selector);
|
||||
|
||||
if ((insertObject_inKeyAtIndex_method || insertKey_atIndexes_method) &&
|
||||
(removeObjectFromKeyAtIndex_method || removeKeyAtIndexes_method))
|
||||
{
|
||||
if (insertObject_inKeyAtIndex_method)
|
||||
{
|
||||
var insertObject_inKeyAtIndex_method_imp = insertObject_inKeyAtIndex_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, insertObject_inKeyAtIndex_selector, function(self, _cmd, anObject, anIndex)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeInsertion
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
|
||||
insertObject_inKeyAtIndex_method_imp(self, _cmd, anObject, anIndex);
|
||||
|
||||
[self didChange:CPKeyValueChangeInsertion
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
if (insertKey_atIndexes_method)
|
||||
{
|
||||
var insertKey_atIndexes_method_imp = insertKey_atIndexes_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, insertKey_atIndexes_selector, function(self, _cmd, objects, indexes)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeInsertion
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
|
||||
insertKey_atIndexes_method_imp(self, _cmd, objects, indexes);
|
||||
|
||||
[self didChange:CPKeyValueChangeInsertion
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
if (removeObjectFromKeyAtIndex_method)
|
||||
{
|
||||
var removeObjectFromKeyAtIndex_method_imp = removeObjectFromKeyAtIndex_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, removeObjectFromKeyAtIndex_selector, function(self, _cmd, anIndex)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeRemoval
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
|
||||
removeObjectFromKeyAtIndex_method_imp(self, _cmd, anIndex);
|
||||
|
||||
[self didChange:CPKeyValueChangeRemoval
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
if (removeKeyAtIndexes_method)
|
||||
{
|
||||
var removeKeyAtIndexes_method_imp = removeKeyAtIndexes_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, removeKeyAtIndexes_selector, function(self, _cmd, indexes)
|
||||
{
|
||||
var isKeyObserved = self._KVOObservedKeys.hasOwnProperty(aKey);
|
||||
|
||||
[self willChange:CPKeyValueChangeRemoval
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
|
||||
removeKeyAtIndexes_method_imp(self, _cmd, indexes);
|
||||
|
||||
if (isKeyObserved)
|
||||
[self didChange:CPKeyValueChangeRemoval
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
// These are optional.
|
||||
var replaceObjectInKeyAtIndex_withObject_selector =
|
||||
sel_getUid("replaceObjectIn" + capitalizedKey + "AtIndex:withObject:"),
|
||||
replaceObjectInKeyAtIndex_withObject_method =
|
||||
class_getInstanceMethod(theClass, replaceObjectInKeyAtIndex_withObject_selector);
|
||||
|
||||
if (replaceObjectInKeyAtIndex_withObject_method)
|
||||
{
|
||||
var replaceObjectInKeyAtIndex_withObject_method_imp =
|
||||
replaceObjectInKeyAtIndex_withObject_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, replaceObjectInKeyAtIndex_withObject_selector,
|
||||
function(self, _cmd, anIndex, anObject)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeReplacement
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
|
||||
replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, anIndex, anObject);
|
||||
|
||||
[self didChange:CPKeyValueChangeReplacement
|
||||
valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
|
||||
var replaceKeyAtIndexes_withKey_selector =
|
||||
sel_getUid("replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":"),
|
||||
replaceKeyAtIndexes_withKey_method =
|
||||
class_getInstanceMethod(theClass, replaceKeyAtIndexes_withKey_selector);
|
||||
|
||||
if (replaceKeyAtIndexes_withKey_method)
|
||||
{
|
||||
var replaceKeyAtIndexes_withKey_method_imp = replaceKeyAtIndexes_withKey_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, replaceKeyAtIndexes_withKey_selector, function(self, _cmd, indexes, objects)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeReplacement
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
|
||||
replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, indexes, objects);
|
||||
|
||||
[self didChange:CPKeyValueChangeReplacement
|
||||
valuesAtIndexes:[indexes copy]
|
||||
forKey:aKey];
|
||||
}, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Unordered To-Many Relationships
|
||||
var addKeyObject_selector = sel_getUid("add" + capitalizedKey + "Object:"),
|
||||
addKeyObject_method = class_getInstanceMethod(theClass, addKeyObject_selector),
|
||||
|
||||
addKey_selector = sel_getUid("add" + capitalizedKey + ":"),
|
||||
addKey_method = class_getInstanceMethod(theClass, addKey_selector),
|
||||
|
||||
removeKeyObject_selector = sel_getUid("remove" + capitalizedKey + "Object:"),
|
||||
removeKeyObject_method = class_getInstanceMethod(theClass, removeKeyObject_selector),
|
||||
|
||||
removeKey_selector = sel_getUid("remove" + capitalizedKey + ":"),
|
||||
removeKey_method = class_getInstanceMethod(theClass, removeKey_selector);
|
||||
|
||||
if ((addKeyObject_method || addKey_method) && (removeKeyObject_method || removeKey_method))
|
||||
{
|
||||
if (addKeyObject_method)
|
||||
{
|
||||
var addKeyObject_method_imp = addKeyObject_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, addKeyObject_selector, function(self, _cmd, anObject)
|
||||
{
|
||||
var isKeyObserved = self._KVOObservedKeys.hasOwnProperty(aKey);
|
||||
|
||||
[self willChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueUnionSetMutation
|
||||
usingObjects:[CPSet setWithObject:anObject]];
|
||||
|
||||
addKeyObject_method_imp(self, _cmd, anObject);
|
||||
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueUnionSetMutation
|
||||
usingObjects:[CPSet setWithObject:anObject]];
|
||||
}, "");
|
||||
}
|
||||
|
||||
if (addKey_method)
|
||||
{
|
||||
var addKey_method_imp = addKey_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, addKey_selector, function(self, _cmd, objects)
|
||||
{
|
||||
[self willChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueUnionSetMutation
|
||||
usingObjects:[objects copy]];
|
||||
|
||||
addKey_method_imp(self, _cmd, objects);
|
||||
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueUnionSetMutation
|
||||
usingObjects:[objects copy]];
|
||||
}, "");
|
||||
}
|
||||
|
||||
if (removeKeyObject_method)
|
||||
{
|
||||
var removeKeyObject_method_imp = removeKeyObject_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, removeKeyObject_selector, function(self, _cmd, anObject)
|
||||
{
|
||||
var isKeyObserved = self._KVOObservedKeys.hasOwnProperty(aKey);
|
||||
|
||||
[self willChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueMinusSetMutation
|
||||
usingObjects:[CPSet setWithObject:anObject]];
|
||||
|
||||
removeKeyObject_method_imp(self, _cmd, anObject);
|
||||
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueMinusSetMutation
|
||||
usingObjects:[CPSet setWithObject:anObject]];
|
||||
}, "");
|
||||
}
|
||||
|
||||
if (removeKey_method)
|
||||
{
|
||||
var removeKey_method_imp = removeKey_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, removeKey_selector, function(self, _cmd, objects)
|
||||
{
|
||||
[self willChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueMinusSetMutation
|
||||
usingObjects:[objects copy]];
|
||||
|
||||
removeKey_method_imp(self, _cmd, objects);
|
||||
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueMinusSetMutation
|
||||
usingObjects:[objects copy]];
|
||||
}, "");
|
||||
}
|
||||
|
||||
// intersect<Key>: is optional.
|
||||
var intersectKey_selector = sel_getUid("intersect" + capitalizedKey + ":"),
|
||||
intersectKey_method = class_getInstanceMethod(theClass, intersectKey_selector);
|
||||
|
||||
if (intersectKey_method)
|
||||
{
|
||||
var intersectKey_method_imp = intersectKey_method.method_imp;
|
||||
|
||||
class_addMethod(KVOClass, intersectKey_selector, function(self, _cmd, aSet)
|
||||
{
|
||||
[self willChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueIntersectSetMutation
|
||||
usingObjects:[aSet copy]];
|
||||
|
||||
intersectKey_method_imp(self, _cmd, aSet);
|
||||
|
||||
[self didChangeValueForKey:aKey
|
||||
withSetMutation:CPKeyValueIntersectSetMutation
|
||||
usingObjects:[aSet copy]];
|
||||
}, "");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -856,127 +1118,5 @@ var _CPKVOInfoMake = function _CPKVOInfoMake(anObserver, theOptions, aContext, a
|
||||
};
|
||||
}
|
||||
|
||||
var _kvoMethodForMethod = function _kvoMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, object)
|
||||
{
|
||||
//FIXME: do we have to call the specific willChange methods for to-many relationships?
|
||||
[self willChangeValueForKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, object);
|
||||
[self didChangeValueForKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoInsertMethodForMethod = function _kvoInsertMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, object, index)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, object, index);
|
||||
[self didChange:CPKeyValueChangeInsertion valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoInsertManyMethodForMethod = function _kvoInsertManyMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, objects, indexes)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeInsertion valuesAtIndexes:indexes forKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, objects, indexes);
|
||||
[self didChange:CPKeyValueChangeInsertion valuesAtIndexes:indexes forKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoReplaceMethodForMethod = function _kvoReplaceMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, index, object)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, index, object);
|
||||
[self didChange:CPKeyValueChangeReplacement valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoReplaceManyMethodForMethod = function _kvoReplaceManyMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, indexes, objects)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeReplacement valuesAtIndexes:indexes forKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, indexes, objects);
|
||||
[self didChange:CPKeyValueChangeReplacement valuesAtIndexes:indexes forKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoRemoveMethodForMethod = function _kvoRemoveMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, index)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, index);
|
||||
[self didChange:CPKeyValueChangeRemoval valuesAtIndexes:[CPIndexSet indexSetWithIndex:index] forKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoRemoveManyMethodForMethod = function _kvoRemoveManyMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, indexes)
|
||||
{
|
||||
[self willChange:CPKeyValueChangeRemoval valuesAtIndexes:indexes forKey:theKey];
|
||||
theMethod.method_imp(self, _cmd, indexes);
|
||||
[self didChange:CPKeyValueChangeRemoval valuesAtIndexes:indexes forKey:theKey];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoUnionMethodForMethod = function _kvoUnionMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, object)
|
||||
{
|
||||
[self willChangeValueForKey:theKey withSetMutation:CPKeyValueUnionSetMutation usingObjects: [CPSet setWithObject: object]];
|
||||
theMethod.method_imp(self, _cmd, object);
|
||||
[self didChangeValueForKey:theKey withSetMutation:CPKeyValueUnionSetMutation usingObjects: [CPSet setWithObject: object]];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoUnionManyMethodForMethod = function _kvoUnionManyMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, objects)
|
||||
{
|
||||
[self willChangeValueForKey:theKey withSetMutation:CPKeyValueUnionSetMutation usingObjects: objects];
|
||||
theMethod.method_imp(self, _cmd, objects);
|
||||
[self didChangeValueForKey:theKey withSetMutation:CPKeyValueUnionSetMutation usingObjects: objects];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoMinusMethodForMethod = function _kvoMinusMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, object)
|
||||
{
|
||||
[self willChangeValueForKey:theKey withSetMutation:CPKeyValueMinusSetMutation usingObjects: [CPSet setWithObject: object]];
|
||||
theMethod.method_imp(self, _cmd, object);
|
||||
[self didChangeValueForKey:theKey withSetMutation:CPKeyValueMinusSetMutation usingObjects: [CPSet setWithObject: object]];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoMinusManyMethodForMethod = function _kvoMinusManyMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, objects)
|
||||
{
|
||||
[self willChangeValueForKey:theKey withSetMutation:CPKeyValueMinusSetMutation usingObjects: objects];
|
||||
theMethod.method_imp(self, _cmd, objects);
|
||||
[self didChangeValueForKey:theKey withSetMutation:CPKeyValueMinusSetMutation usingObjects: objects];
|
||||
}
|
||||
}
|
||||
|
||||
var _kvoIntersectManyMethodForMethod = function _kvoIntersectManyMethodForMethod(theKey, theMethod)
|
||||
{
|
||||
return function(self, _cmd, objects)
|
||||
{
|
||||
[self willChangeValueForKey:theKey withSetMutation:CPKeyValueIntersectSetMutation usingObjects: objects];
|
||||
theMethod.method_imp(self, _cmd, objects);
|
||||
[self didChangeValueForKey:theKey withSetMutation:CPKeyValueIntersectSetMutation usingObjects: objects];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@import "CPArray+KVO.j"
|
||||
@import "CPSet+KVO.j"
|
||||
Reference in new issue
Block a user