Ich versuche, eine einfache Objektbrücke in Kakao zu implementieren, wo das Brückenobjekt als eine kvo/bindings-konforme Einfügung für eine beliebige andere NSObject-Instanz fungiert.Implementieren eines KVO/Bindings-Compliant Bridge-Patterns in Cocoa
Hier ist mein Problem (mehr Details im Code unten):
Eine Brücke Objekt wirkt wie ein Tropfen in einer Person-Objekt mit einer Eigenschaft NSString * Name und eine Adresse * Eigenschaft genannt Adresse. Die Bindung an den keyPath "Name" oder "Adresse" der Bridge funktioniert gut. Die Problembehandlung beginnt, wenn ein Objekt an den keyPath "address.street" der Bridge gebunden wird und ein neues Adressobjekt für die Adresse der Person festgelegt wird. Das ergibt KVO bedingte Ausnahmen, die wie folgt aussehen:
Cannot remove an observer <NSKeyValueObservance 0x126b00> for the key path "street" from <Address 0x12f1d0> because it is not registered as an observer
Dies geschieht, obwohl die Brücke bemerkt die Änderung in der „Adresse“ -Grundstück und gibt ein willChangeValueForKeyPath/didChangeValueForKeyPath Tupels.
Der folgende Code erzeugt das Problem. Es ist in sich geschlossene Objective-C-Code, der in einer Datei „BridgeDemo.m“ und kompiliert Lauf mit
gcc -o test BridgeDemo.m -framework AppKit -framework Foundation; ./test
gespeichert werden können Wenn Sie eine Lösung für dieses Problem kennen oder kann mir einen besseren Ansatz bieten die gleiche Problemlösung Sie machen mich ein sehr glücklich Programmierer!
BridgeDemo.m:
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
/* --- Address ----------------------------------------- */
@interface Address : 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])) { return nil; }
self.street = @"Elm Street";
self.zipCode = @"12345";
self.city = @"Crashington";
return self;
}
-(void) modifyStreet {
self.street = @"Main Street";
}
-(void)dealloc {
[street release]; [zipCode release]; [city release];
[super dealloc];
}
@end
/* --- Person ----------------------------------------- */
@interface Person : 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])) { return nil; }
self.name = @"Tom";
self.address = [[Address new] autorelease];
return self;
}
- (void)modifyAddress {
Address* a = [[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
/* --- Bridge ----------------------------------------- */
@interface Bridge : NSObject {
NSMutableDictionary* observedKeys;
NSObject* obj;
}
@property(retain) NSObject* obj;
@end
@implementation Bridge
@synthesize obj;
- (id)init {
if(!(self = [super init])) { return nil; }
observedKeys = [NSMutableDictionary new];
return self;
}
- (void)forwardInvocation:(NSInvocation*)inv {
[inv invokeWithTarget:obj];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
return [obj methodSignatureForSelector:aSelector];
}
- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
NSLog(@">>>> Detected Change in keyPath: %@", keyPath);
[self willChangeValueForKey:keyPath];
[self didChangeValueForKey:keyPath];
}
-(id)valueForUndefinedKey:(NSString*)key {
/* Register an observer for the key, if not already done */
if(![observedKeys objectForKey:key]) {
[observedKeys setObject:[NSNumber numberWithBool:YES] forKey:key];
[obj addObserver:self forKeyPath:key options:NSKeyValueObservingOptionNew context:nil];
}
return [obj valueForKey:key];
}
- (void)dealloc {
for(NSString* key in [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
/* This works fine */
void testBindingToAddress() {
NSLog(@"Testing Binding to '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];
}
/* This produces an exception */
void testBindingToStreet() {
NSLog(@"Testing Binding to '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 main (int argc, const char * argv[]) {
testBindingToAddress();
testBindingToStreet();
return 0;
}