API  1.0.0
CPKeyValueObserving.j
Go to the documentation of this file.
1 /*
2  * CPKeyValueObserving.j
3  * Foundation
4  *
5  * Created by Ross Boucher.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
25 
26 - (void)willChangeValueForKey:(CPString)aKey
27 {
28  if (!aKey)
29  return;
30 
31  if (!self[KVOProxyKey])
32  {
33  if (!self._willChangeMessageCounter)
34  self._willChangeMessageCounter = new Object();
35 
36  if (!self._willChangeMessageCounter[aKey])
37  self._willChangeMessageCounter[aKey] = 1;
38  else
39  self._willChangeMessageCounter[aKey] += 1;
40  }
41 }
42 
43 - (void)didChangeValueForKey:(CPString)aKey
44 {
45  if (!aKey)
46  return;
47 
48  if (!self[KVOProxyKey])
49  {
50  if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
51  {
52  self._willChangeMessageCounter[aKey] -= 1;
53 
54  if (!self._willChangeMessageCounter[aKey])
55  delete self._willChangeMessageCounter[aKey];
56  }
57  else
58  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
59  }
60 }
61 
62 - (void)willChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
63 {
64  if (!aKey)
65  return;
66 
67  if (!self[KVOProxyKey])
68  {
69  if (!self._willChangeMessageCounter)
70  self._willChangeMessageCounter = new Object();
71 
72  if (!self._willChangeMessageCounter[aKey])
73  self._willChangeMessageCounter[aKey] = 1;
74  else
75  self._willChangeMessageCounter[aKey] += 1;
76  }
77 }
78 
79 - (void)didChange:(CPKeyValueChange)aChange valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
80 {
81  if (!aKey)
82  return;
83 
84  if (!self[KVOProxyKey])
85  {
86  if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
87  {
88  self._willChangeMessageCounter[aKey] -= 1;
89 
90  if (!self._willChangeMessageCounter[aKey])
91  delete self._willChangeMessageCounter[aKey];
92  }
93  else
94  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
95  }
96 }
97 
98 - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
99 {
100  if (!aKey)
101  return;
102 
103  if (!self[KVOProxyKey])
104  {
105  if (!self._willChangeMessageCounter)
106  self._willChangeMessageCounter = new Object();
107 
108  if (!self._willChangeMessageCounter[aKey])
109  self._willChangeMessageCounter[aKey] = 1;
110  else
111  self._willChangeMessageCounter[aKey] += 1;
112  }
113 }
114 
115 - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)aMutationKind usingObjects:(CPSet)objects
116 {
117  if (!self[KVOProxyKey])
118  {
119  if (self._willChangeMessageCounter && self._willChangeMessageCounter[aKey])
120  {
121  self._willChangeMessageCounter[aKey] -= 1;
122 
123  if (!self._willChangeMessageCounter[aKey])
124  delete self._willChangeMessageCounter[aKey];
125  }
126  else
127  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
128  }
129 }
130 
131 - (void)addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(CPKeyValueObservingOptions)options context:(id)aContext
132 {
133  if (!anObserver || !aPath)
134  return;
135 
136  [[_CPKVOProxy proxyForObject:self] _addObserver:anObserver forKeyPath:aPath options:options context:aContext];
137 }
138 
139 - (void)removeObserver:(id)anObserver forKeyPath:(CPString)aPath
140 {
141  if (!anObserver || !aPath)
142  return;
143 
144  [self[KVOProxyKey] _removeObserver:anObserver forKeyPath:aPath];
145 }
146 
157 + (BOOL)automaticallyNotifiesObserversForKey:(CPString)aKey
158 {
159  var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
160  selector = "automaticallyNotifiesObserversOf" + capitalizedKey,
161  aClass = [self class];
162 
163  if ([aClass respondsToSelector:selector])
164  return aClass.isa.objj_msgSend0(aClass, selector);
165 
166  return YES;
167 }
168 
169 + (CPSet)keyPathsForValuesAffectingValueForKey:(CPString)aKey
170 {
171  var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1),
172  selector = "keyPathsForValuesAffecting" + capitalizedKey,
173  aClass = [self class];
174 
175  if ([aClass respondsToSelector:selector])
176  return aClass.isa.objj_msgSend0(aClass, selector);
177 
178  return [CPSet set];
179 }
180 
181 - (void)applyChange:(CPDictionary)aChange toKeyPath:(CPString)aKeyPath
182 {
183  var changeKind = [aChange objectForKey:CPKeyValueChangeKindKey],
184  oldValue = [aChange objectForKey:CPKeyValueChangeOldKey],
185  newValue = [aChange objectForKey:CPKeyValueChangeNewKey];
186 
187  if (newValue === [CPNull null])
188  newValue = nil;
189 
190  if (changeKind === CPKeyValueChangeSetting)
191  return [self setValue:newValue forKeyPath:aKeyPath];
192 
193  var indexes = [aChange objectForKey:CPKeyValueChangeIndexesKey];
194 
195  // If we have an indexes entry, then we have an ordered to-many relationship
196  if (indexes)
197  {
198  if (changeKind === CPKeyValueChangeInsertion)
199  [[self mutableArrayValueForKeyPath:aKeyPath] insertObjects:newValue atIndexes:indexes];
200 
201  else if (changeKind === CPKeyValueChangeRemoval)
202  [[self mutableArrayValueForKeyPath:aKeyPath] removeObjectsAtIndexes:indexes];
203 
204  else if (changeKind === CPKeyValueChangeReplacement)
205  [[self mutableArrayValueForKeyPath:aKeyPath] replaceObjectsAtIndexes:indexes withObjects:newValue];
206  }
207  else
208  {
209  if (changeKind === CPKeyValueChangeInsertion)
210  [[self mutableSetValueForKeyPath:aKeyPath] unionSet:newValue];
211 
212  else if (changeKind === CPKeyValueChangeRemoval)
213  [[self mutableSetValueForKeyPath:aKeyPath] minusSet:oldValue];
214 
215  else if (changeKind === CPKeyValueChangeReplacement)
216  [[self mutableSetValueForKeyPath:aKeyPath] setSet:newValue];
217  }
218 }
219 
220 @end
221 
223 
224 - (CPDictionary)inverseChangeDictionary
225 {
226  var inverseChangeDictionary = [self mutableCopy],
227  changeKind = [self objectForKey:CPKeyValueChangeKindKey];
228 
229  if (changeKind === CPKeyValueChangeSetting || changeKind === CPKeyValueChangeReplacement)
230  {
232  setObject:[self objectForKey:CPKeyValueChangeOldKey]
233  forKey:CPKeyValueChangeNewKey];
234 
236  setObject:[self objectForKey:CPKeyValueChangeNewKey]
237  forKey:CPKeyValueChangeOldKey];
238  }
239 
240  else if (changeKind === CPKeyValueChangeInsertion)
241  {
243  setObject:CPKeyValueChangeRemoval
244  forKey:CPKeyValueChangeKindKey];
245 
247  setObject:[self objectForKey:CPKeyValueChangeNewKey]
248  forKey:CPKeyValueChangeOldKey];
249 
250  [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeNewKey];
251  }
252 
253  else if (changeKind === CPKeyValueChangeRemoval)
254  {
256  setObject:CPKeyValueChangeInsertion
257  forKey:CPKeyValueChangeKindKey];
258 
260  setObject:[self objectForKey:CPKeyValueChangeOldKey]
261  forKey:CPKeyValueChangeNewKey];
262 
263  [inverseChangeDictionary removeObjectForKey:CPKeyValueChangeOldKey];
264  }
265 
266  return inverseChangeDictionary;
267 }
268 
269 @end
270 
271 // KVO Options
276 
277 // KVO Change Dictionary Keys
278 CPKeyValueChangeKindKey = @"CPKeyValueChangeKindKey";
279 CPKeyValueChangeNewKey = @"CPKeyValueChangeNewKey";
280 CPKeyValueChangeOldKey = @"CPKeyValueChangeOldKey";
281 CPKeyValueChangeIndexesKey = @"CPKeyValueChangeIndexesKey";
282 CPKeyValueChangeNotificationIsPriorKey = @"CPKeyValueChangeNotificationIsPriorKey";
283 
284 // KVO Change Types
289 
290 // CPKeyValueSetMutationKind
295 
296 //FIXME: "secret" dict ivar-keys are workaround to support unordered to-many relationships without too many modifications
297 _CPKeyValueChangeSetMutationObjectsKey = @"_CPKeyValueChangeSetMutationObjectsKey";
298 _CPKeyValueChangeSetMutationKindKey = @"_CPKeyValueChangeSetMutationKindKey";
299 _CPKeyValueChangeSetMutationNewValueKey = @"_CPKeyValueChangeSetMutationNewValueKey";
300 
301 var _changeKindForSetMutationKind = function(mutationKind)
302 {
303  switch (mutationKind)
304  {
309  }
310 };
311 
313  DependentKeysKey = "$KVODEPENDENT",
314  KVOProxyKey = "$KVOPROXY";
315 
316 //rule of thumb: _ methods are called on the real proxy object, others are called on the "fake" proxy object (aka the real object)
317 
318 /* @ignore */
319 @implementation _CPKVOProxy : CPObject
320 {
321  id _targetObject;
322  Class _nativeClass;
323  CPDictionary _changesForKey;
324  CPDictionary _nestingForKey;
325  CPDictionary _minOptionsForKey;
326  Object _observersForKey;
327  int _observersForKeyLength;
328  CPSet _replacedKeys;
329 
330  // TODO: Remove this line when granular notifications are implemented
331  BOOL _adding;
332 }
333 
334 + (id)proxyForObject:(CPObject)anObject
335 {
336  var proxy = anObject[KVOProxyKey];
337 
338  if (proxy)
339  return proxy;
340 
341  return [[self alloc] initWithTarget:anObject];
342 }
343 
344 - (id)initWithTarget:(id)aTarget
345 {
346  if (self = [super init])
347  {
348  _targetObject = aTarget;
349  _nativeClass = [aTarget class];
350  _observersForKey = {};
351  _changesForKey = {};
352  _nestingForKey = {};
353  _minOptionsForKey = {};
354  _observersForKeyLength = 0;
355 
356  [self _replaceClass];
357  aTarget[KVOProxyKey] = self;
358  }
359  return self;
360 }
361 
362 - (void)_replaceClass
363 {
364  var currentClass = _nativeClass,
365  kvoClassName = "$KVO_" + class_getName(_nativeClass),
366  existingKVOClass = objj_lookUpClass(kvoClassName);
367 
368  if (existingKVOClass)
369  {
370  _targetObject.isa = existingKVOClass;
371  _replacedKeys = existingKVOClass._replacedKeys;
372  return;
373  }
374 
375  var kvoClass = objj_allocateClassPair(currentClass, kvoClassName);
376 
377  objj_registerClassPair(kvoClass);
378 
379  _replacedKeys = [CPSet set];
380  kvoClass._replacedKeys = _replacedKeys;
381 
382  //copy in the methods from our model subclass
383  var methods = class_copyMethodList(_CPKVOModelSubclass);
384 
385  if ([_targetObject isKindOfClass:[CPDictionary class]])
386  methods = methods.concat(class_copyMethodList(_CPKVOModelDictionarySubclass));
387 
388  class_addMethods(kvoClass, methods);
389 
390  _targetObject.isa = kvoClass;
391 }
392 
393 - (void)_replaceModifiersForKey:(CPString)aKey
394 {
395  if (![_replacedKeys containsObject:aKey] && [_nativeClass automaticallyNotifiesObserversForKey:aKey])
396  {
397  [_replacedKeys addObject:aKey];
398 
399  var theClass = _nativeClass,
400  KVOClass = _targetObject.isa,
401  capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substring(1);
402 
403  // Attribute and To-One Relationships
404  var setKey_selector = sel_getUid("set" + capitalizedKey + ":"),
405  setKey_method = class_getInstanceMethod(theClass, setKey_selector);
406 
407  if (setKey_method)
408  {
409  var setKey_method_imp = setKey_method.method_imp;
410 
411  class_addMethod(KVOClass, setKey_selector, function(self, _cmd, anObject)
412  {
413  [self willChangeValueForKey:aKey];
414 
415  setKey_method_imp(self, _cmd, anObject);
416 
417  [self didChangeValueForKey:aKey];
418  }, setKey_method.method_types);
419  }
420 
421  // FIXME: Deprecated.
422  var _setKey_selector = sel_getUid("_set" + capitalizedKey + ":"),
423  _setKey_method = class_getInstanceMethod(theClass, _setKey_selector);
424 
425  if (_setKey_method)
426  {
427  var _setKey_method_imp = _setKey_method.method_imp;
428 
429  class_addMethod(KVOClass, _setKey_selector, function(self, _cmd, anObject)
430  {
431  [self willChangeValueForKey:aKey];
432 
433  _setKey_method_imp(self, _cmd, anObject);
434 
435  [self didChangeValueForKey:aKey];
436  }, _setKey_method.method_types);
437  }
438 
439  // Ordered To-Many Relationships
440  var insertObject_inKeyAtIndex_selector = sel_getUid("insertObject:in" + capitalizedKey + "AtIndex:"),
441  insertObject_inKeyAtIndex_method =
442  class_getInstanceMethod(theClass, insertObject_inKeyAtIndex_selector),
443 
444  insertKey_atIndexes_selector = sel_getUid("insert" + capitalizedKey + ":atIndexes:"),
445  insertKey_atIndexes_method =
446  class_getInstanceMethod(theClass, insertKey_atIndexes_selector),
447 
448  removeObjectFromKeyAtIndex_selector = sel_getUid("removeObjectFrom" + capitalizedKey + "AtIndex:"),
449  removeObjectFromKeyAtIndex_method =
450  class_getInstanceMethod(theClass, removeObjectFromKeyAtIndex_selector),
451 
452  removeKeyAtIndexes_selector = sel_getUid("remove" + capitalizedKey + "AtIndexes:"),
453  removeKeyAtIndexes_method = class_getInstanceMethod(theClass, removeKeyAtIndexes_selector);
454 
455  if ((insertObject_inKeyAtIndex_method || insertKey_atIndexes_method) &&
456  (removeObjectFromKeyAtIndex_method || removeKeyAtIndexes_method))
457  {
458  if (insertObject_inKeyAtIndex_method)
459  {
460  var insertObject_inKeyAtIndex_method_imp = insertObject_inKeyAtIndex_method.method_imp;
461 
462  class_addMethod(KVOClass, insertObject_inKeyAtIndex_selector, function(self, _cmd, anObject, anIndex)
463  {
464  [self willChange:CPKeyValueChangeInsertion
465  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
466  forKey:aKey];
467 
468  insertObject_inKeyAtIndex_method_imp(self, _cmd, anObject, anIndex);
469 
470  [self didChange:CPKeyValueChangeInsertion
471  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
472  forKey:aKey];
473  }, insertObject_inKeyAtIndex_method.method_types);
474  }
475 
476  if (insertKey_atIndexes_method)
477  {
478  var insertKey_atIndexes_method_imp = insertKey_atIndexes_method.method_imp;
479 
480  class_addMethod(KVOClass, insertKey_atIndexes_selector, function(self, _cmd, objects, indexes)
481  {
482  [self willChange:CPKeyValueChangeInsertion
483  valuesAtIndexes:[indexes copy]
484  forKey:aKey];
485 
486  insertKey_atIndexes_method_imp(self, _cmd, objects, indexes);
487 
488  [self didChange:CPKeyValueChangeInsertion
489  valuesAtIndexes:[indexes copy]
490  forKey:aKey];
491  }, insertKey_atIndexes_method.method_types);
492  }
493 
494  if (removeObjectFromKeyAtIndex_method)
495  {
496  var removeObjectFromKeyAtIndex_method_imp = removeObjectFromKeyAtIndex_method.method_imp;
497 
498  class_addMethod(KVOClass, removeObjectFromKeyAtIndex_selector, function(self, _cmd, anIndex)
499  {
500  [self willChange:CPKeyValueChangeRemoval
501  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
502  forKey:aKey];
503 
504  removeObjectFromKeyAtIndex_method_imp(self, _cmd, anIndex);
505 
506  [self didChange:CPKeyValueChangeRemoval
507  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
508  forKey:aKey];
509  }, removeObjectFromKeyAtIndex_method.method_types);
510  }
511 
512  if (removeKeyAtIndexes_method)
513  {
514  var removeKeyAtIndexes_method_imp = removeKeyAtIndexes_method.method_imp;
515 
516  class_addMethod(KVOClass, removeKeyAtIndexes_selector, function(self, _cmd, indexes)
517  {
518  [self willChange:CPKeyValueChangeRemoval
519  valuesAtIndexes:[indexes copy]
520  forKey:aKey];
521 
522  removeKeyAtIndexes_method_imp(self, _cmd, indexes);
523 
524  [self didChange:CPKeyValueChangeRemoval
525  valuesAtIndexes:[indexes copy]
526  forKey:aKey];
527  }, removeKeyAtIndexes_method.method_types);
528  }
529 
530  // These are optional.
531  var replaceObjectInKeyAtIndex_withObject_selector =
532  sel_getUid("replaceObjectIn" + capitalizedKey + "AtIndex:withObject:"),
533  replaceObjectInKeyAtIndex_withObject_method =
534  class_getInstanceMethod(theClass, replaceObjectInKeyAtIndex_withObject_selector);
535 
536  if (replaceObjectInKeyAtIndex_withObject_method)
537  {
538  var replaceObjectInKeyAtIndex_withObject_method_imp =
539  replaceObjectInKeyAtIndex_withObject_method.method_imp;
540 
541  class_addMethod(KVOClass, replaceObjectInKeyAtIndex_withObject_selector,
542  function(self, _cmd, anIndex, anObject)
543  {
544  [self willChange:CPKeyValueChangeReplacement
545  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
546  forKey:aKey];
547 
548  replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, anIndex, anObject);
549 
550  [self didChange:CPKeyValueChangeReplacement
551  valuesAtIndexes:[CPIndexSet indexSetWithIndex:anIndex]
552  forKey:aKey];
553  }, replaceObjectInKeyAtIndex_withObject_method.method_types);
554  }
555 
556  var replaceKeyAtIndexes_withKey_selector =
557  sel_getUid("replace" + capitalizedKey + "AtIndexes:with" + capitalizedKey + ":"),
558  replaceKeyAtIndexes_withKey_method =
559  class_getInstanceMethod(theClass, replaceKeyAtIndexes_withKey_selector);
560 
561  if (replaceKeyAtIndexes_withKey_method)
562  {
563  var replaceKeyAtIndexes_withKey_method_imp = replaceKeyAtIndexes_withKey_method.method_imp;
564 
565  class_addMethod(KVOClass, replaceKeyAtIndexes_withKey_selector, function(self, _cmd, indexes, objects)
566  {
567  [self willChange:CPKeyValueChangeReplacement
568  valuesAtIndexes:[indexes copy]
569  forKey:aKey];
570 
571  replaceObjectInKeyAtIndex_withObject_method_imp(self, _cmd, indexes, objects);
572 
573  [self didChange:CPKeyValueChangeReplacement
574  valuesAtIndexes:[indexes copy]
575  forKey:aKey];
576  }, replaceKeyAtIndexes_withKey_method.method_types);
577  }
578  }
579 
580  // Unordered To-Many Relationships
581  var addKeyObject_selector = sel_getUid("add" + capitalizedKey + "Object:"),
582  addKeyObject_method = class_getInstanceMethod(theClass, addKeyObject_selector),
583 
584  addKey_selector = sel_getUid("add" + capitalizedKey + ":"),
585  addKey_method = class_getInstanceMethod(theClass, addKey_selector),
586 
587  removeKeyObject_selector = sel_getUid("remove" + capitalizedKey + "Object:"),
588  removeKeyObject_method = class_getInstanceMethod(theClass, removeKeyObject_selector),
589 
590  removeKey_selector = sel_getUid("remove" + capitalizedKey + ":"),
591  removeKey_method = class_getInstanceMethod(theClass, removeKey_selector);
592 
593  if ((addKeyObject_method || addKey_method) && (removeKeyObject_method || removeKey_method))
594  {
595  if (addKeyObject_method)
596  {
597  var addKeyObject_method_imp = addKeyObject_method.method_imp;
598 
599  class_addMethod(KVOClass, addKeyObject_selector, function(self, _cmd, anObject)
600  {
601  [self willChangeValueForKey:aKey
602  withSetMutation:CPKeyValueUnionSetMutation
603  usingObjects:[CPSet setWithObject:anObject]];
604 
605  addKeyObject_method_imp(self, _cmd, anObject);
606 
607  [self didChangeValueForKey:aKey
608  withSetMutation:CPKeyValueUnionSetMutation
609  usingObjects:[CPSet setWithObject:anObject]];
610  }, addKeyObject_method.method_types);
611  }
612 
613  if (addKey_method)
614  {
615  var addKey_method_imp = addKey_method.method_imp;
616 
617  class_addMethod(KVOClass, addKey_selector, function(self, _cmd, objects)
618  {
619  [self willChangeValueForKey:aKey
620  withSetMutation:CPKeyValueUnionSetMutation
621  usingObjects:[objects copy]];
622 
623  addKey_method_imp(self, _cmd, objects);
624 
625  [self didChangeValueForKey:aKey
626  withSetMutation:CPKeyValueUnionSetMutation
627  usingObjects:[objects copy]];
628  }, addKey_method.method_types);
629  }
630 
631  if (removeKeyObject_method)
632  {
633  var removeKeyObject_method_imp = removeKeyObject_method.method_imp;
634 
635  class_addMethod(KVOClass, removeKeyObject_selector, function(self, _cmd, anObject)
636  {
637  [self willChangeValueForKey:aKey
638  withSetMutation:CPKeyValueMinusSetMutation
639  usingObjects:[CPSet setWithObject:anObject]];
640 
641  removeKeyObject_method_imp(self, _cmd, anObject);
642 
643  [self didChangeValueForKey:aKey
644  withSetMutation:CPKeyValueMinusSetMutation
645  usingObjects:[CPSet setWithObject:anObject]];
646  }, removeKeyObject_method.method_types);
647  }
648 
649  if (removeKey_method)
650  {
651  var removeKey_method_imp = removeKey_method.method_imp;
652 
653  class_addMethod(KVOClass, removeKey_selector, function(self, _cmd, objects)
654  {
655  [self willChangeValueForKey:aKey
656  withSetMutation:CPKeyValueMinusSetMutation
657  usingObjects:[objects copy]];
658 
659  removeKey_method_imp(self, _cmd, objects);
660 
661  [self didChangeValueForKey:aKey
662  withSetMutation:CPKeyValueMinusSetMutation
663  usingObjects:[objects copy]];
664  }, removeKey_method.method_types);
665  }
666 
667  // intersect<Key>: is optional.
668  var intersectKey_selector = sel_getUid("intersect" + capitalizedKey + ":"),
669  intersectKey_method = class_getInstanceMethod(theClass, intersectKey_selector);
670 
671  if (intersectKey_method)
672  {
673  var intersectKey_method_imp = intersectKey_method.method_imp;
674 
675  class_addMethod(KVOClass, intersectKey_selector, function(self, _cmd, aSet)
676  {
677  [self willChangeValueForKey:aKey
678  withSetMutation:CPKeyValueIntersectSetMutation
679  usingObjects:[aSet copy]];
680 
681  intersectKey_method_imp(self, _cmd, aSet);
682 
683  [self didChangeValueForKey:aKey
684  withSetMutation:CPKeyValueIntersectSetMutation
685  usingObjects:[aSet copy]];
686  }, intersectKey_method.method_types);
687  }
688  }
689  }
690 
691  var affectingKeys = [[_nativeClass keyPathsForValuesAffectingValueForKey:aKey] allObjects],
692  affectingKeysCount = affectingKeys ? affectingKeys.length : 0;
693 
694  if (!affectingKeysCount)
695  return;
696 
697  var dependentKeysForClass = _nativeClass[DependentKeysKey];
698 
699  if (!dependentKeysForClass)
700  {
701  dependentKeysForClass = {};
702  _nativeClass[DependentKeysKey] = dependentKeysForClass;
703  }
704 
705  while (affectingKeysCount--)
706  {
707  var affectingKey = affectingKeys[affectingKeysCount],
708  affectedKeys = dependentKeysForClass[affectingKey];
709 
710  if (!affectedKeys)
711  {
712  affectedKeys = [CPSet new];
713  dependentKeysForClass[affectingKey] = affectedKeys;
714  }
715 
716  [affectedKeys addObject:aKey];
717 
718  //observe key paths of objects other then ourselves, so we are notified of the changes
719  //use CPKeyValueObservingOptionPrior to ensure proper wrapping around changes
720  //so CPKeyValueObservingOptionPrior and CPKeyValueObservingOptionOld can be fulfilled even for dependent keys
721  if (affectingKey.indexOf(@".") !== -1)
722  [_targetObject addObserver:self forKeyPath:affectingKey options:CPKeyValueObservingOptionPrior | kvoNewAndOld context:nil];
723  else
724  [self _replaceModifiersForKey:affectingKey];
725  }
726 }
727 
728 - (void)observeValueForKeyPath:(CPString)theKeyPath ofObject:(id)theObject change:(CPDictionary)theChanges context:(id)theContext
729 {
730  // Fire change events for the dependent keys
731  var dependentKeysForClass = _nativeClass[DependentKeysKey],
732  dependantKeys = [dependentKeysForClass[theKeyPath] allObjects],
733  isBeforeFlag = !![theChanges objectForKey:CPKeyValueChangeNotificationIsPriorKey];
734 
735  for (var i = 0; i < [dependantKeys count]; i++)
736  {
737  var dependantKey = [dependantKeys objectAtIndex:i];
738  [self _sendNotificationsForKey:dependantKey changeOptions:theChanges isBefore:isBeforeFlag];
739  }
740 }
741 
742 - (void)_addObserver:(id)anObserver forKeyPath:(CPString)aPath options:(CPKeyValueObservingOptions)options context:(id)aContext
743 {
744  if (!anObserver)
745  return;
746 
747  var forwarder = nil;
748 
749  if (aPath.indexOf('.') !== CPNotFound && aPath.charAt(0) !== '@')
750  forwarder = [[_CPKVOForwardingObserver alloc] initWithKeyPath:aPath object:_targetObject observer:anObserver options:options context:aContext];
751  else
752  [self _replaceModifiersForKey:aPath];
753 
754  var observers = _observersForKey[aPath];
755 
756  if (!observers)
757  {
758  observers = @{};
759  _observersForKey[aPath] = observers;
760  _observersForKeyLength++;
761  }
762 
763  [observers setObject:_CPKVOInfoMake(anObserver, options, aContext, forwarder) forKey:[anObserver UID]];
764 
765  if (options & CPKeyValueObservingOptionInitial)
766  {
767  var changes;
768 
769  if (options & CPKeyValueObservingOptionNew)
770  {
771  var newValue = [_targetObject valueForKeyPath:aPath];
772 
773  if (newValue == nil)
774  newValue = [CPNull null];
775 
777  } else {
779  }
780 
781  [anObserver observeValueForKeyPath:aPath ofObject:_targetObject change:changes context:aContext];
782  }
783 }
784 
785 - (void)_removeObserver:(id)anObserver forKeyPath:(CPString)aPath
786 {
787  var observers = _observersForKey[aPath];
788 
789  if (!observers)
790  {
791  // TODO: Remove this line when granular notifications are implemented
792  if (!_adding)
793  CPLog.warn(@"Cannot remove an observer %@ for the key path \"%@\" from %@ because it is not registered as an observer.", _targetObject, aPath, anObserver);
794 
795  return;
796  }
797 
798  if (aPath.indexOf('.') != CPNotFound)
799  {
800  // During cib instantiation, it is possible for the forwarder to not yet be available,
801  // so we have to check for nil.
802  var observer = [observers objectForKey:[anObserver UID]],
803  forwarder = observer ? observer.forwarder : nil;
804 
805  [forwarder finalize];
806  }
807 
808  [observers removeObjectForKey:[anObserver UID]];
809 
810  if (![observers count])
811  {
812  _observersForKeyLength--;
813  delete _observersForKey[aPath];
814  }
815 
816  if (!_observersForKeyLength)
817  {
818  _targetObject.isa = _nativeClass; //restore the original class
819  delete _targetObject[KVOProxyKey];
820  }
821 }
822 
823 //FIXME: We do not compute and cache if CPKeyValueObservingOptionOld is needed, so we may do unnecessary work
824 
825 - (void)_sendNotificationsForKey:(CPString)aKey changeOptions:(CPDictionary)changeOptions isBefore:(BOOL)isBefore
826 {
827  var changes = _changesForKey[aKey],
828  observers = [_observersForKey[aKey] allValues],
829  observersMinimumOptions = 0;
830 
831  if (isBefore)
832  {
833  if (changes)
834  {
835  // "willChange:X" nesting.
836  var level = _nestingForKey[aKey];
837 
838  if (!level)
839  [CPException raise:CPInternalInconsistencyException reason:@"_changesForKey without _nestingForKey"];
840 
841  _nestingForKey[aKey] = level + 1;
842  // Only notify on the first willChange..., silently note any following nested calls.
843  return;
844  }
845 
846  _nestingForKey[aKey] = 1;
847 
848  // Get the combined minimum of the ...Old and ...New options for all observers
849  var count = observers ? observers.length : 0;
850 
851  while (count--)
852  {
853  var observerInfo = observers[count];
854 
855  observersMinimumOptions |= observerInfo.options & kvoNewAndOld;
856  }
857 
858  _minOptionsForKey[aKey] = observersMinimumOptions;
859  changes = changeOptions;
860 
861  if (observersMinimumOptions & CPKeyValueObservingOptionOld)
862  {
863  var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
864  setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey];
865 
866  if (setMutationKind)
867  {
868  var setMutationObjects = [changes[_CPKeyValueChangeSetMutationObjectsKey] copy],
869  setExistingObjects = [[_targetObject valueForKey: aKey] copy];
870 
871  if (setMutationKind == CPKeyValueMinusSetMutation)
872  {
873  [setExistingObjects intersectSet: setMutationObjects];
874  [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey];
875  }
876  else if (setMutationKind === CPKeyValueIntersectSetMutation || setMutationKind === CPKeyValueSetSetMutation)
877  {
878  [setExistingObjects minusSet: setMutationObjects];
879  [changes setValue:setExistingObjects forKey:CPKeyValueChangeOldKey];
880  }
881 
882  //for unordered to-many relationships (CPSet) even new values can only be calculated before!!!
883  if (setMutationKind === CPKeyValueUnionSetMutation || setMutationKind === CPKeyValueSetSetMutation)
884  {
885  [setMutationObjects minusSet: setExistingObjects];
886  //hide new value (for CPKeyValueObservingOptionPrior messages)
887  //as long as "didChangeValue..." is not yet called!
888  changes[_CPKeyValueChangeSetMutationNewValueKey] = setMutationObjects;
889  }
890  }
891  else if (indexes)
892  {
893  var type = [changes objectForKey:CPKeyValueChangeKindKey];
894 
895  // for ordered to-many relationships, oldvalue is only sensible for replace and remove
896  if (type === CPKeyValueChangeReplacement || type === CPKeyValueChangeRemoval)
897  {
898  //FIXME: do we need to go through and replace "" with CPNull?
899  var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
900  [changes setValue:newValues forKey:CPKeyValueChangeOldKey];
901  }
902  }
903  else
904  {
905  var oldValue = [_targetObject valueForKey:aKey];
906 
907  if (oldValue === nil || oldValue === undefined)
908  oldValue = [CPNull null];
909 
910  [changes setObject:oldValue forKey:CPKeyValueChangeOldKey];
911  }
912 
913  }
914 
915  [changes setObject:1 forKey:CPKeyValueChangeNotificationIsPriorKey];
916  _changesForKey[aKey] = changes;
917 
918  // Clear ...New option as it should never be sent for a ...Prior option
919  observersMinimumOptions &= ~CPKeyValueObservingOptionNew;
920  }
921  else
922  {
923  var level = _nestingForKey[aKey];
924 
925  if (!changes || !level)
926  {
927  if (_targetObject._willChangeMessageCounter && _targetObject._willChangeMessageCounter[aKey])
928  {
929  // Close unobserved willChange for a given key.
930  _targetObject._willChangeMessageCounter[aKey] -= 1;
931 
932  if (!_targetObject._willChangeMessageCounter[aKey])
933  delete _targetObject._willChangeMessageCounter[aKey];
934 
935  return;
936  }
937  else
938  [CPException raise:@"CPKeyValueObservingException" reason:@"'didChange...' message called without prior call of 'willChange...'"];
939  }
940 
941  _nestingForKey[aKey] = level - 1;
942 
943  if (level - 1 > 0)
944  {
945  // willChange... was called multiple times. Only fire observation notifications when
946  // didChange... has been called an equal number of times.
947  return;
948  }
949 
950  delete _nestingForKey[aKey];
951 
952  [changes removeObjectForKey:CPKeyValueChangeNotificationIsPriorKey];
953 
954  observersMinimumOptions = _minOptionsForKey[aKey];
955 
956  if (observersMinimumOptions & CPKeyValueObservingOptionNew)
957  {
958  var indexes = [changes objectForKey:CPKeyValueChangeIndexesKey],
959  setMutationKind = changes[_CPKeyValueChangeSetMutationKindKey];
960 
961  if (setMutationKind)
962  {
963  //old and new values for unordered to-many relationships can only be calculated before
964  //set recalculated hidden new value as soon as "didChangeValue..." is called!
965  var newValue = changes[_CPKeyValueChangeSetMutationNewValueKey];
966  [changes setValue:newValue forKey:CPKeyValueChangeNewKey];
967 
968  //delete hidden values
969  delete changes[_CPKeyValueChangeSetMutationNewValueKey];
970  delete changes[_CPKeyValueChangeSetMutationObjectsKey];
971  delete changes[_CPKeyValueChangeSetMutationKindKey];
972  }
973  else if (indexes)
974  {
975  var type = [changes objectForKey:CPKeyValueChangeKindKey];
976 
977  // for ordered to-many relationships, newvalue is only sensible for replace and insert
979  {
980  //FIXME: do we need to go through and replace "" with CPNull?
981  var newValues = [[_targetObject mutableArrayValueForKeyPath:aKey] objectsAtIndexes:indexes];
982  [changes setValue:newValues forKey:CPKeyValueChangeNewKey];
983  }
984  }
985  else
986  {
987  var newValue = [_targetObject valueForKey:aKey];
988 
989  if (newValue === nil || newValue === undefined)
990  newValue = [CPNull null];
991 
992  [changes setObject:newValue forKey:CPKeyValueChangeNewKey];
993  }
994  }
995 
996  delete _minOptionsForKey[aKey];
997  delete _changesForKey[aKey];
998  }
999 
1000  var count = observers ? observers.length : 0,
1001  changesCache = {};
1002 
1003  while (count--)
1004  {
1005  var observerInfo = observers[count],
1006  options = observerInfo.options,
1007  onlyNewAndOldOptions = options & kvoNewAndOld,
1008  observerChanges = nil;
1009 
1010  if (isBefore)
1011  {
1012  // Only send 'observeValueForKeyPath:' for '...Prior' option when handling 'willChangeValue...'
1013  if (options & CPKeyValueObservingOptionPrior)
1014  {
1015  observerChanges = changes;
1016  // The new values are not yet created in the change dictionary so remove ...New option to get a working cache below
1017  onlyNewAndOldOptions &= ~CPKeyValueObservingOptionNew;
1018  }
1019  }
1020  else
1021  {
1022  observerChanges = changes;
1023  }
1024 
1025  if (observerChanges)
1026  {
1027  // Don't change the 'change' dictionary when the observer wants the minimum options.
1028  // The ...New option is remved above for the ...Prior case
1029  if (onlyNewAndOldOptions !== observersMinimumOptions)
1030  {
1031  // Use a subset of the 'change' dictionary. First try to find it in the cache
1032  observerChanges = changesCache[onlyNewAndOldOptions];
1033  if (!observerChanges)
1034  {
1035  // Not in the cache. Build a new dictionary and store it in the cache
1036  changesCache[onlyNewAndOldOptions] = observerChanges = [changes mutableCopy];
1037  if (!(onlyNewAndOldOptions & CPKeyValueObservingOptionOld))
1038  [observerChanges removeObjectForKey:CPKeyValueChangeOldKey];
1039  if (!(onlyNewAndOldOptions & CPKeyValueObservingOptionNew))
1040  [observerChanges removeObjectForKey:CPKeyValueChangeNewKey];
1041  }
1042  }
1043  [observerInfo.observer observeValueForKeyPath:aKey ofObject:_targetObject change:observerChanges context:observerInfo.context];
1044  }
1045  }
1046 
1047  var dependentKeysMap = _nativeClass[DependentKeysKey];
1048 
1049  if (!dependentKeysMap)
1050  return;
1051 
1052  var dependentKeyPaths = [dependentKeysMap[aKey] allObjects];
1053 
1054  if (!dependentKeyPaths)
1055  return;
1056 
1057  var index = 0,
1058  count = [dependentKeyPaths count];
1059 
1060  for (; index < count; ++index)
1061  {
1062  var keyPath = dependentKeyPaths[index];
1063 
1064  [self _sendNotificationsForKey:keyPath
1065  changeOptions:isBefore ? [changeOptions copy] : _changesForKey[keyPath]
1066  isBefore:isBefore];
1067  }
1068 }
1069 
1070 @end
1071 @implementation _CPKVOModelSubclass : CPObject
1072 {
1073  id __doxygen__;
1074 }
1075 
1076 - (void)willChangeValueForKey:(CPString)aKey
1077 {
1078  var superClass = [self class],
1079  methodSelector = @selector(willChangeValueForKey:),
1080  methodImp = class_getMethodImplementation(superClass, methodSelector);
1081 
1082  methodImp(self, methodSelector, aKey);
1083 
1084  if (!aKey)
1085  return;
1086 
1087  var changeOptions = @{ CPKeyValueChangeKindKey: CPKeyValueChangeSetting };
1088 
1089  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
1090 }
1091 
1092 - (void)didChangeValueForKey:(CPString)aKey
1093 {
1094  var superClass = [self class],
1095  methodSelector = @selector(didChangeValueForKey:),
1096  methodImp = class_getMethodImplementation(superClass, methodSelector);
1097 
1098  methodImp(self, methodSelector, aKey);
1099 
1100  if (!aKey)
1101  return;
1102 
1103  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
1104 }
1105 
1106 - (void)willChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
1107 {
1108  var superClass = [self class],
1109  methodSelector = @selector(willChange:valuesAtIndexes:forKey:),
1110  methodImp = class_getMethodImplementation(superClass, methodSelector);
1111 
1112  methodImp(self, methodSelector, change, indexes, aKey);
1113 
1114  if (!aKey)
1115  return;
1116 
1117  var changeOptions = @{ CPKeyValueChangeKindKey: change, CPKeyValueChangeIndexesKey: indexes };
1118 
1119  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
1120 }
1121 
1122 - (void)didChange:(CPKeyValueChange)change valuesAtIndexes:(CPIndexSet)indexes forKey:(CPString)aKey
1123 {
1124  var superClass = [self class],
1125  methodSelector = @selector(didChange:valuesAtIndexes:forKey:),
1126  methodImp = class_getMethodImplementation(superClass, methodSelector);
1127 
1128  methodImp(self, methodSelector, change, indexes, aKey);
1129 
1130  if (!aKey)
1131  return;
1132 
1133  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
1134 }
1135 
1136 - (void)willChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
1137 {
1138  var superClass = [self class],
1139  methodSelector = @selector(willChangeValueForKey:withSetMutation:usingObjects:),
1140  methodImp = class_getMethodImplementation(superClass, methodSelector);
1141 
1142  methodImp(self, methodSelector, aKey, mutationKind, objects);
1143 
1144  if (!aKey)
1145  return;
1146 
1147  var changeKind = _changeKindForSetMutationKind(mutationKind),
1148  changeOptions = @{ CPKeyValueChangeKindKey: changeKind };
1149 
1150  //set hidden change-dict ivars to support unordered to-many relationships
1151  changeOptions[_CPKeyValueChangeSetMutationObjectsKey] = objects;
1152  changeOptions[_CPKeyValueChangeSetMutationKindKey] = mutationKind;
1153 
1154  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:changeOptions isBefore:YES];
1155 }
1156 
1157 - (void)didChangeValueForKey:(CPString)aKey withSetMutation:(CPKeyValueSetMutationKind)mutationKind usingObjects:(CPSet)objects
1158 {
1159  var superClass = [self class],
1160  methodSelector = @selector(didChangeValueForKey:withSetMutation:usingObjects:),
1161  methodImp = class_getMethodImplementation(superClass, methodSelector);
1162 
1163  methodImp(self, methodSelector, aKey, mutationKind, objects);
1164 
1165  if (!aKey)
1166  return;
1167 
1168  [[_CPKVOProxy proxyForObject:self] _sendNotificationsForKey:aKey changeOptions:nil isBefore:NO];
1169 }
1170 
1171 - (Class)class
1172 {
1173  return self[KVOProxyKey]._nativeClass;
1174 }
1175 
1176 - (Class)superclass
1177 {
1178  return [[self class] superclass];
1179 }
1180 
1181 - (BOOL)isKindOfClass:(Class)aClass
1182 {
1183  return [[self class] isSubclassOfClass:aClass];
1184 }
1185 
1186 - (BOOL)isMemberOfClass:(Class)aClass
1187 {
1188  return [self class] == aClass;
1189 }
1190 
1191 - (CPString)className
1192 {
1193  return [self class].name;
1194 }
1195 
1196 @end
1197 @implementation _CPKVOModelDictionarySubclass : CPObject
1198 {
1199  id __doxygen__;
1200 }
1201 
1202 - (void)removeAllObjects
1203 {
1204  var keys = [self allKeys],
1205  count = [keys count],
1206  i = 0;
1207 
1208  for (; i < count; i++)
1209  [self willChangeValueForKey:keys[i]];
1210 
1211  var superClass = [self class],
1212  methodSelector = @selector(removeAllObjects),
1213  methodImp = class_getMethodImplementation(superClass, methodSelector);
1214 
1215  methodImp(self, methodSelector);
1216 
1217  for (i = 0; i < count; i++)
1218  [self didChangeValueForKey:keys[i]];
1219 }
1220 
1221 - (void)removeObjectForKey:(id)aKey
1222 {
1223  [self willChangeValueForKey:aKey];
1224 
1225  var superClass = [self class],
1226  methodSelector = @selector(removeObjectForKey:),
1227  methodImp = class_getMethodImplementation(superClass, methodSelector);
1228 
1229  methodImp(self, methodSelector, aKey);
1230 
1231  [self didChangeValueForKey:aKey];
1232 }
1233 
1234 - (void)setObject:(id)anObject forKey:(id)aKey
1235 {
1236  [self willChangeValueForKey:aKey];
1237 
1238  var superClass = [self class],
1239  methodSelector = @selector(setObject:forKey:),
1240  methodImp = class_getMethodImplementation(superClass, methodSelector);
1241 
1242  methodImp(self, methodSelector, anObject, aKey);
1243 
1244  [self didChangeValueForKey:aKey];
1245 }
1246 
1247 @end
1248 
1249 @implementation _CPKVOForwardingObserver : CPObject
1250 {
1251  id _object;
1252  id _observer;
1253  id _context;
1254  unsigned _options;
1255  //a.b
1256  CPString _firstPart; //a
1257  CPString _secondPart; //b
1258 
1259  id _value;
1260 }
1261 
1262 - (id)initWithKeyPath:(CPString)aKeyPath object:(id)anObject observer:(id)anObserver options:(unsigned)options context:(id)aContext
1263 {
1264  self = [super init];
1265 
1266  _context = aContext;
1267  _observer = anObserver;
1268  _object = anObject;
1269  _options = options;
1270 
1271  var dotIndex = aKeyPath.indexOf('.');
1272 
1273  if (dotIndex === CPNotFound)
1274  [CPException raise:CPInvalidArgumentException reason:"Created _CPKVOForwardingObserver without compound key path: " + aKeyPath];
1275 
1276  _firstPart = aKeyPath.substring(0, dotIndex);
1277  _secondPart = aKeyPath.substring(dotIndex + 1);
1278 
1279  //become an observer of the first part of our key (a)
1280  [_object addObserver:self forKeyPath:_firstPart options:_options context:nil];
1281 
1282  //the current value of a (not the value of a.b)
1283  _value = [_object valueForKey:_firstPart];
1284 
1285  if (_value)
1286  [_value addObserver:self forKeyPath:_secondPart options:_options context:nil]; //we're observing b on current a
1287 
1288  return self;
1289 }
1290 
1291 - (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)changes context:(id)aContext
1292 {
1293  if (aKeyPath === _firstPart)
1294  {
1295  var pathChanges = [CPMutableDictionary dictionaryWithObject:CPKeyValueChangeSetting forKey:CPKeyValueChangeKindKey];
1296  var isBeforeFlag = !![changes objectForKey:CPKeyValueChangeNotificationIsPriorKey];
1297 
1298  if (isBeforeFlag)
1299  [pathChanges setObject:1 forKey:CPKeyValueChangeNotificationIsPriorKey];
1300 
1301  if (_options & CPKeyValueObservingOptionOld)
1302  {
1303  var oldValue = [_value valueForKeyPath:_secondPart];
1304 
1305  [pathChanges setObject:oldValue != null ? oldValue : [CPNull null] forKey:CPKeyValueChangeOldKey];
1306  }
1307 
1308  if (!isBeforeFlag && (_options & CPKeyValueObservingOptionNew))
1309  {
1310  var newValue = [_object valueForKeyPath:_firstPart + "." + _secondPart];
1311 
1312  [pathChanges setObject:newValue != null ? newValue : [CPNull null] forKey:CPKeyValueChangeNewKey];
1313  }
1314 
1315  [_observer observeValueForKeyPath:_firstPart + "." + _secondPart ofObject:_object change:pathChanges context:_context];
1316 
1317  // Nothing has changed yet when doing willChange....
1318  if (!isBeforeFlag) {
1319  //since a has changed, we should remove ourselves as an observer of the old a, and observe the new one
1320  if (_value)
1321  [_value removeObserver:self forKeyPath:_secondPart];
1322 
1323  _value = [_object valueForKey:_firstPart];
1324 
1325  if (_value)
1326  [_value addObserver:self forKeyPath:_secondPart options:_options context:nil];
1327  }
1328  }
1329  else
1330  {
1331  //a is the same, but a.b has changed -- nothing to do but forward this message along
1332  [_observer observeValueForKeyPath:_firstPart + "." + aKeyPath ofObject:_object change:changes context:_context];
1333  }
1334 }
1335 
1336 - (void)finalize
1337 {
1338  if (_value)
1339  [_value removeObserver:self forKeyPath:_secondPart];
1340 
1341  [_object removeObserver:self forKeyPath:_firstPart];
1342 
1343  _object = nil;
1344  _observer = nil;
1345  _context = nil;
1346  _value = nil;
1347 }
1348 
1349 @end
1350 
1351 var _CPKVOInfoMake = function(anObserver, theOptions, aContext, aForwarder)
1352 {
1353  return {
1354  observer: anObserver,
1355  options: theOptions,
1356  context: aContext,
1357  forwarder: aForwarder
1358  };
1359 };
1360 
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
CPKeyValueChangeInsertion
CPKeyValueChangeOldKey
id init()
Definition: CALayer.j:126
CPKeyValueObservingOptionPrior
An object representation of nil.
Definition: CPNull.h:2
var DependentKeysKey
CPDictionary inverseChangeDictionary()
CPKeyValueChangeReplacement
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
A collection of unique integers.
Definition: CPIndexSet.h:2
void removeObjectForKey:(id aKey)
Definition: CPDictionary.j:555
id mutableSetValueForKeyPath:(id aKeyPath)
Definition: CPSet+KVO.j:31
CPKeyValueChangeRemoval
void setValue:forKeyPath:(id aValue, [forKeyPath] CPString aKeyPath)
A mutable key-value pair collection.
Definition: CPDictionary.h:2
CPKeyValueChangeKindKey
CPKeyValueObservingOptionInitial
CPKeyValueChangeSetting
An immutable string (collection of characters).
Definition: CPString.h:2
CPNull null()
Definition: CPNull.j:51
CPKeyValueChangeIndexesKey
id objectForKey:(id aKey)
Definition: CPDictionary.j:515
CPKeyValueIntersectSetMutation
CPKeyValueObservingOptionNew
var KVOProxyKey
CPKeyValueChangeNewKey
CPKeyValueChangeNotificationIsPriorKey
CPNotFound
Definition: CPObjJRuntime.j:62
CPKeyValueUnionSetMutation
var kvoNewAndOld
id mutableArrayValueForKeyPath:(id aKeyPath)
Definition: CPArray+KVO.j:32
Class class()
Definition: CPObject.j:179
CPKeyValueMinusSetMutation
id indexSetWithIndex:(int anIndex)
Definition: CPIndexSet.j:51
CPKeyValueSetSetMutation
void setObject:forKey:(id anObject, [forKey] id aKey)
Definition: CPDictionary.j:589
id mutableCopy()
Definition: CPObject.j:163
CPDictionary copy()
Definition: CPDictionary.j:292
CPKeyValueObservingOptionOld
id dictionaryWithObject:forKey:(id anObject, [forKey] id aKey)
Definition: CPDictionary.j:81