18

W moim modelu mam tablicę obiektów zwanych zdarzeniami. Chciałbym, aby mój kontroler był powiadamiany o każdym dodaniu nowego obiektu do wydarzeń.Obserwowanie zmian w tablicy zmiennych przy użyciu KVO vs. NSNotificationCenter

Myślałem, że to dobry sposób, aby zrobić to byłoby użyć deseniu KVO, aby otrzymywać powiadomienia, gdy wydarzenia zmiany (z nowego obiektu dodawanych)

// AppDelegate 
// events is a NSMutableArray @property/@synthesize etc... 

[appDelagate addObserver:self 
       forKeyPath:@"events" 
        options:NSKeyValueObservingOptionNew 
        context:NULL]; 

Ale metoda observeValueForKeyPath nie było miano i odkryłem, że tablice nie są zgodne KVO :-(

Jedną z opcji jest ręcznie wywołać metodę wywołując willChangeValueForKey dla keypath

// ViewController 
[self willChangeValueForKey:@"events"]; 
[self.events addObject:event]; 
[self didChangeValueForKey:@"events"]; 

Ale to czuje się ciężki ponieważ mam prawdopodobnie również śledzić stan przed i po mojej tablicy zdarzeń tak, że może on być dostępny z metodą observeValueForKeyPath.

Jednym ze sposobów może być użycie standardowej tablicy (zamiast zmiennego) i utworzenie/ustawienie nowej instancji zdarzeń za każdym razem, gdy chcę dodać nowy obiekt, lub mogę utworzyć oddzielną właściwość, która śledzi liczbę elementy są w tablicy zmiennych (chciałbym, abyś mógł zaobserwować @ "events.count").

Inną opcją byłoby użycie NSNotificationCenter. Przeczytałem również kilka odpowiedzi, które sugerują użycie bloków (ale nie mam pojęcia, od czego zacząć).

W końcu, czy mogę zachować instancję kontrolera u mojego delegata i po prostu wysłać odpowiednią wiadomość?

// Delegate 
[myController eventsDidChange]; 

Czy posiadanie odniesienia do kontrolera przez delegata jest dziwne?

Staram się zrozumieć, jak wybrać najlepsze podejście do użytkowania, więc wszelkie porady dotyczące wydajności, przyszłej elastyczności kodu i najlepszych praktyk są mile widziane!

Odpowiedz

18

Nie powinieneś tworzyć bezpośrednich właściwości publicznych zbiorów zmiennych, aby uniknąć ich mutacji bez Twojej wiedzy. NSArray nie jest kluczową wartością, którą można zaobserwować, ale twoja jeden-do-wielu własność@"events" jest. Oto jak go przestrzegać:

Najpierw zadeklarować własność publiczną o niezmiennej kolekcji:

@interface Model 
@property (nonatomic, copy) NSArray *events; 
@end 

Następnie w implementacji powrotem go z zmienny ivar:

@interface Model() 
{ 
    NSMutableArray *_events; 
} 
@end 

i zastąpić getter i seter:

@implementation Model 

@synthesize events = _events; 

- (NSArray *)events 
{ 
    return [_events copy]; 
} 

- (void)setEvents:(NSArray *)events 
{ 
    if ([_events isEqualToArray:events] == NO) 
    { 
     _events = [events mutableCopy]; 
    } 
} 

@end 

Jeśli do Twojego modelu potrzebne są inne obiekty, należy dodać może uzyskać zmienny obiekt proxy, wywołując -[Model mutableArrayValueForKey:@"events"].

NSMutableArray *events = [modelInstance mutableArrayValueForKey:@"events"]; 
[events addObject:newEvent]; 

Spowoduje to uruchomienie powiadomień KVO przez ustawienie właściwości za każdym razem przy użyciu nowej kolekcji.Aby uzyskać lepszą wydajność i bardziej szczegółową kontrolę, zaimplementuj pozostałą część modelu array accessors.

Zobacz także: Observing an NSMutableArray for insertion/removal.

+2

Dziękujemy! mutableArrayValueForKey wykonuje lewę. Czy masz jakieś wskazówki, jak wybrać wzór do użycia (KVO, NotificationCenter, delegata), kiedy chcesz komunikować się między modelem a kontrolerem? – MathewS

+0

Na pewno czegoś tutaj brakuje. Czy ktoś mógłby wyjaśnić, gdzie setEvents zostałoby wywołane, gdyby ktoś dodał lub insertObject: atIndex: obiekt do tablicy tablicy zmiennej? –

0

Per na docs on accessor methods, należy wdrożyć:

- (void)addEventsObject:(Event*)e 
{ 
    [_events addObject:e]; 
} 

- (void)removeEventsObject:(Event*)e 
{ 
    [_events removeObject:e]; 
} 

Następnie KVO zadziała powiadomienia, gdy są to tzw.