| #import <Foundation/Foundation.h> |
| |
| // SourceBase will be the base class of Source. We'll pass a Source object into a |
| // function as a SourceBase, and then see if the dynamic typing can get us through the KVO |
| // goo and all the way back to Source. |
| |
| @interface SourceBase: NSObject |
| { |
| uint32_t _value; |
| } |
| - (SourceBase *) init; |
| - (uint32_t) getValue; |
| @end |
| |
| @implementation SourceBase |
| - (SourceBase *) init |
| { |
| [super init]; |
| _value = 10; |
| return self; |
| } |
| - (uint32_t) getValue |
| { |
| return _value; |
| } |
| @end |
| |
| // Source is a class that will be observed by the Observer class below. |
| // When Observer sets itself up to observe this property (in initWithASource) |
| // the KVO system will overwrite the "isa" pointer of the object with the "kvo'ed" |
| // one. |
| |
| @interface Source : SourceBase |
| { |
| int _property; |
| } |
| - (Source *) init; |
| - (void) setProperty: (int) newValue; |
| @end |
| |
| @implementation Source |
| - (Source *) init |
| { |
| [super init]; |
| _property = 20; |
| return self; |
| } |
| - (void) setProperty: (int) newValue |
| { |
| _property = newValue; // This is the line in setProperty, make sure we step to here. |
| } |
| @end |
| |
| @interface SourceDerived : Source |
| { |
| int _derivedValue; |
| } |
| - (SourceDerived *) init; |
| - (uint32_t) getValue; |
| @end |
| |
| @implementation SourceDerived |
| - (SourceDerived *) init |
| { |
| [super init]; |
| _derivedValue = 30; |
| return self; |
| } |
| - (uint32_t) getValue |
| { |
| return _derivedValue; |
| } |
| @end |
| |
| // Observer is the object that will watch Source and cause KVO to swizzle it... |
| |
| @interface Observer : NSObject |
| { |
| Source *_source; |
| } |
| + (Observer *) observerWithSource: (Source *) source; |
| - (Observer *) initWithASource: (Source *) source; |
| - (void) observeValueForKeyPath: (NSString *) path |
| ofObject: (id) object |
| change: (NSDictionary *) change |
| context: (void *) context; |
| @end |
| |
| @implementation Observer |
| |
| + (Observer *) observerWithSource: (Source *) inSource; |
| { |
| Observer *retval; |
| |
| retval = [[Observer alloc] initWithASource: inSource]; |
| return retval; |
| } |
| |
| - (Observer *) initWithASource: (Source *) source |
| { |
| [super init]; |
| _source = source; |
| [_source addObserver: self |
| forKeyPath: @"property" |
| options: (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld) |
| context: NULL]; |
| return self; |
| } |
| |
| - (void) observeValueForKeyPath: (NSString *) path |
| ofObject: (id) object |
| change: (NSDictionary *) change |
| context: (void *) context |
| { |
| printf ("Observer function called.\n"); |
| return; |
| } |
| @end |
| |
| uint32_t |
| handle_SourceBase (SourceBase *object) |
| { |
| return [object getValue]; // Break here to check dynamic values. |
| } |
| |
| int main () |
| { |
| Source *mySource; |
| Observer *myObserver; |
| |
| NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; |
| |
| mySource = [[SourceDerived alloc] init]; |
| myObserver = [Observer observerWithSource: mySource]; |
| |
| [mySource setProperty: 5]; // Break here to see if we can step into real method. |
| |
| uint32_t return_value = handle_SourceBase (mySource); |
| |
| SourceDerived *unwatchedSource = [[SourceDerived alloc] init]; |
| |
| return_value = handle_SourceBase (unwatchedSource); |
| |
| [pool release]; |
| return 0; |
| |
| } |