Ich sehe ein eigenartiges Verhalten mit Cocoas KVC/KVO und Bindungen. Ich habe ein NSArrayController
Objekt, mit seinem 'Inhalt' an eine NSMutableArray
gebunden, und ich habe einen Controller als Beobachter der arrangedObjects
Eigenschaft auf der NSArrayController
registriert. Mit diesem Setup erwarte ich, dass jedes Mal, wenn das Array geändert wird, eine KVO-Benachrichtigung erhalten wird. Es scheint jedoch, dass die KVO-Benachrichtigung nur einmal gesendet wird; Das erste Mal, wenn das Array geändert wird.KVC/KVO und Bindings: Warum erhalte ich nur eine Änderungsbenachrichtigung?
Ich habe ein brandneues "Cocoa Application" -Projekt in Xcode eingerichtet, um das Problem zu veranschaulichen. Hier ist mein Code:
BindingTesterAppDelegate.h
#import <Cocoa/Cocoa.h>
@interface BindingTesterAppDelegate : NSObject <NSApplicationDelegate>
{
NSWindow * window;
NSArrayController * arrayController;
NSMutableArray * mutableArray;
}
@property (assign) IBOutlet NSWindow * window;
@property (retain) NSArrayController * arrayController;
@property (retain) NSMutableArray * mutableArray;
- (void)changeArray:(id)sender;
@end
BindingTesterAppDelegate.m
#import "BindingTesterAppDelegate.h"
@implementation BindingTesterAppDelegate
@synthesize window;
@synthesize arrayController;
@synthesize mutableArray;
- (void)applicationDidFinishLaunching:(NSNotification *)notification
{
NSLog(@"load");
// create the array controller and the mutable array:
[self setArrayController:[[[NSArrayController alloc] init] autorelease]];
[self setMutableArray:[NSMutableArray arrayWithCapacity:0]];
// bind the arrayController to the array
[arrayController bind:@"content" // see update
toObject:self
withKeyPath:@"mutableArray"
options:0];
// set up an observer for arrangedObjects
[arrayController addObserver:self
forKeyPath:@"arrangedObjects"
options:0
context:nil];
// add a button to trigger events
NSButton * button = [[NSButton alloc]
initWithFrame:NSMakeRect(10, 10, 100, 30)];
[[window contentView] addSubview:button];
[button setTitle:@"change array"];
[button setTarget:self];
[button setAction:@selector(changeArray:)];
[button release];
NSLog(@"run");
}
- (void)changeArray:(id)sender
{
// modify the array (being sure to post KVO notifications):
[self willChangeValueForKey:@"mutableArray"];
[mutableArray addObject:[NSString stringWithString:@"something"]];
NSLog(@"changed the array: count = %d", [mutableArray count]);
[self didChangeValueForKey:@"mutableArray"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
NSLog(@"%@ changed!", keyPath);
}
- (void)applicationWillTerminate:(NSNotification *)notification
{
NSLog(@"stop");
[self setMutableArray:nil];
[self setArrayController:nil];
NSLog(@"done");
}
@end
Und hier ist die Ausgabe:
load
run
changed the array: count = 1
arrangedObjects changed!
changed the array: count = 2
changed the array: count = 3
changed the array: count = 4
changed the array: count = 5
stop
arrangedObjects changed!
done
Wie Sie sehen können , das Die KVO-Benachrichtigung wird nur beim ersten Mal gesendet (und noch einmal, wenn die Anwendung beendet wird). Warum sollte das der Fall sein?
Update:
Dank orque für den Hinweis auf, dass ich auf die contentArray
meiner NSArrayController
verbindlich sein sollte, nicht nur seine content
. Die oben gepostet Code funktioniert, sobald diese Änderung vorgenommen wird:
// bind the arrayController to the array
[arrayController bind:@"contentArray" // <-- the change was made here
toObject:self
withKeyPath:@"mutableArray"
options:0];
+1 und danke für eine sehr detaillierte Antwort. Ich habe meine Bindung an "contentArray" anstelle von "content" geändert, und alles hat wie ein Zauber funktioniert. Zum Ändern des Arrays durch den Controller: Dies war ein vereinfachtes Beispiel. In meiner realen Anwendung ist das Array eine Eigenschaft eines Modellobjekts und wird durch einen anderen Prozess modifiziert. Wenn ich den arrayController verwenden würde, um mein Objekt zu ändern, müssten meine Model-Klassen an meine Controller-Klassen gekoppelt werden, was völlig gegen das MVC-Muster verstößt. –
Die Tastenaktion wäre 'add:', nicht 'addObject:' (was die Schaltfläche hinzufügen würde!). –