implementing kvo/bindings-compliant bridge-pattern cocoa
i'm perplexing exercise rudimentary vigilant overpass cocoa where overpass vigilant acts kvo/bindings-compliant dump erratic nsobject instance.
here problem (more sum formula below):
a overpass vigilant acts dump person-object, an nsstring* ability called name an address* ability address. contracting keypath "name" "address" overpass works nicely. problem starts contracting vigilant keypath "address.street" overpass new address-object set person's address property. law kvo-related exceptions feeling this:
cannot mislay an witness <nskeyvalueobservance 0x126b00> pivotal route "street" <address 0x12f1d0> since purebred an observer
this happens even nonetheless overpass notices change "address"-property emits willchangevalueforkeypath/didchangevalueforkeypath tuple.
the formula next produces the problem. it's self-contained objective-c formula saved record "bridgedemo.m" collected run with
gcc -o exam bridgedemo.m -framework appkit -framework foundation; ./test
if know fortitude problem offer me improved proceed clarify same problem me very happy programmer!
bridgedemo.m:
#import <foundation/foundation.h>
#import <appkit/appkit.h>
/* --- castle ----------------------------------------- */
@interface castle : nsobject {
nsstring* street;
nsnumber* zipcode;
nsstring* city;
}
@property(retain) nsstring* street;
@property(retain) nsnumber* zipcode;
@property(retain) nsstring* city;
@end
@implementation address
@synthesize street, zipcode, city;
-(id)init {
if( !( self = [super init] ) ) { relapse nil; }
self.street = @"elm street";
self.zipcode = @"12345";
self.city = @"crashington";
relapse self;
}
-(void) modifystreet {
self.street = @"main street";
}
-(void)dealloc {
[street release]; [zipcode release]; [city release];
[super dealloc];
}
@end
/* --- chairman ----------------------------------------- */
@interface chairman : nsobject {
nsstring* name;
address* address;
}
@property(retain) nsstring* name;
@property(retain) address* address;
@end
@implementation person
@synthesize address, name;
-(id)init {
if( !( self = [super init] ) ) { relapse nil; }
self.name = @"tom";
self.address = [[address new] autorelease];
relapse self;
}
- (void)modifyaddress {
address* = [[address new] autorelease];
a.street = @"jump street";
a.zipcode = @"54321";
a.city = @"memleakville";
self.address = a;
}
- (void)dealloc { [address release]; [name release]; [super dealloc]; }
@end
/* --- overpass ----------------------------------------- */
@interface overpass : nsobject {
nsmutabledictionary* observedkeys;
nsobject* obj;
}
@property(retain) nsobject* obj;
@end
@implementation bridge
@synthesize obj;
- (id)init {
if( !( self = [super init] ) ) { relapse nil; }
observedkeys = [nsmutabledictionary new];
relapse self;
}
- (void)forwardinvocation:(nsinvocation*)inv {
[inv invokewithtarget:obj];
}
- (nsmethodsignature *)methodsignatureforselector:(sel)aselector {
relapse [obj methodsignatureforselector:aselector];
}
- (void) observevalueforkeypath:(nsstring *)keypath ofobject:(id)object change:(nsdictionary *)change context:(void *)context
{
nslog( @">>>> detected change keypath: %@", keypath );
[self willchangevalueforkey:keypath];
[self didchangevalueforkey:keypath];
}
-(id)valueforundefinedkey:(nsstring*)key {
/* register an witness key, already finished */
if( ![observedkeys objectforkey:key] ) {
[observedkeys setobject:[nsnumber numberwithbool:yes] forkey:key];
[obj addobserver:self forkeypath:key options:nskeyvalueobservingoptionnew context:nil];
}
relapse [obj valueforkey:key];
}
- (void)dealloc {
for( nsstring* pivotal [observedkeys allkeys] ) {
[obj removeobserver:self forkeypath:key];
}
[obj release];
[observedkeys release];
[super dealloc];
}
@end
/* --- myobserver ------------------------------------ */
@interface myobserver : nsobject {
address* address;
nsstring* street;
}
@property(retain) address* address;
@property(retain) nsstring* street;
@end
@implementation myobserver
@synthesize street, address;
-(void)dealloc { [street release]; [super dealloc]; }
@end
/* works glorious */
void testbindingtoaddress() {
nslog( @"testing contracting 'address' --------------" );
nsautoreleasepool * pool = [[nsautoreleasepool alloc] init];
bridge* b = [[bridge new] autorelease];
b.obj = [person new];
myobserver* o = [[myobserver new] autorelease];
[o bind:@"address" toobject:b withkeypath:@"address"
options:nil];
nslog( @"before modifystreet: %@", o.address.street );
[[b valueforkey:@"address"] performselector:@selector(modifystreet)];
nslog( @"after modifystreet: %@", o.address.street );
[b performselector:@selector(modifyaddress)];
nslog( @"after modifyadress: %@", o.address.street );
[pool drain];
}
/* produces an difference */
void testbindingtostreet() {
nslog( @"testing contracting 'address.street' --------------" );
nsautoreleasepool * pool = [[nsautoreleasepool alloc] init];
bridge* b = [[bridge new] autorelease];
b.obj = [person new];
myobserver* o = [[myobserver new] autorelease];
[o bind:@"street" toobject:b withkeypath:@"address.street"
options:nil];
nslog( @"before modifystreet: %@", o.street );
[[b valueforkey:@"address"] performselector:@selector(modifystreet)];
nslog( @"after modifystreet: %@", o.street );
[b performselector:@selector(modifyaddress)];
nslog( @"after modifyadress: %@", o.street );
[pool drain];
}
/* --- main() ------------------------------------ */
int sure (int argc, const bake * argv[]) {
testbindingtoaddress();
testbindingtostreet();
relapse 0;
}
Comments
Post a Comment