2012-02-13 11 views
6

Le mie classi di modello sono principalmente implementate con metodi setter/getter sintetizzati e tutto andava bene. Tutto era ben collegato all'interfaccia utente. In seguito mi sono reso conto che la modifica di una proprietà avrebbe dovuto modificare altre proprietà (la modifica di type potrebbe causare modifiche in minA e maxA), quindi ho scritto manualmente i metodi setter/getter per la proprietà type. Codice segue:Perché ottengo un'eccezione non rilevata quando si implementano i miei metodi setter/getter KVC

QBElementType

@interface QBElementType : NSObject 
    @property NSRange minRange; 
    @property NSRange maxRange; 
@end 

@implementation QBElementType 
    @synthesize minRange; 
    @synthesize maxRange; 
@end 

QBElement

@interface QBElement : QBElementType{ 
    QBElementType *_type; 
} 
    @property (weak) QBElementType *type; 
    @property NSUInteger minA; 
    @property NSUInteger maxA; 
@end 


@implementation QBElement 
    @synthesize minA = _minA; 
    @synthesize maxA = _maxA; 

- (QBElementType*)type 
{ 
    return _type; 
} 
- (void)setType:(QBElementType*)newType 
{ 
    [self willChangeValueForKey:@"type"]; 
    _type = newType; // no need to bother with retain/release due to ARC. 
    [self didChangeValueForKey:@"type"]; 

    /* Having the following 4 lines commented out or not changes nothing to the error 
    if (!NSLocationInRange(_minA, newType.minRange)) 
     self.minA = newType.minRange.location; 
    if (!NSLocationInRange(_maxA, newType.maxRange)) 
     self.maxA = newType.maxRange.location; 
    */ 
} 
@end 

DOMANDA: Da allora, ogni volta che cambia il tipo di un elemento ottengo un un'eccezione non rilevata:

Impossibile aggiornare per osservatore < NSTableBinder ...> per il percorso della chiave "type.minRange" dal < QBElement ...>, molto probabilmente perché il valore per il "tipo" chiave è stata cambiata senza un adeguato KVO notifica inviata. Controllare la conformità KVO della classe QBElement.

C'è qualcosa di ovvio che mi manca da KVO-Compliance? Perché ottengo questo errore?

risposta

3

Non è necessario chiamare esplicitamente -willChangeValueForKey: e -didChangeValueForKey: perché il setter è denominato correttamente. Le chiamate a loro saranno aggiunte automaticamente attraverso la magia delle macchine KVC/KVO. Il problema potrebbe essere che vengono effettivamente chiamati due volte.

Inoltre, poiché è emerso che minA e maxA sono semplicemente derivato dal tipo, è possibile fare in sola lettura e dire KVO di notifica automatica degli osservatori che Mina e Maxa sono cambiati in qualsiasi momento type modifiche:

@interface QBElement : QBElementType{ 
    QBElementType *_type; 
} 
    @property (weak) QBElementType *type; 
    @property (readonly) NSUInteger minA; 
    @property (readonly) NSUInteger maxA; 
@end 

@implementation QBElement 

+ (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key 
{ 
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; 

    if ([key isEqualToString:@"minA"] || 
     [key isEqualToString:@"maxA"]) { 
     keyPaths = [keyPaths setByAddingObject:@"type"]; 
    } 

    return keyPaths; 
} 

@synthesize type; 

- (NSUInteger)minA 
{ 
    return self.type.minRange.location; 
} 

- (NSUInteger)maxA 
{ 
    return self.type.maxRange.location; 
} 

@end 
+0

Ho provato a eseguire l'app senza 'willChangeValueForKey:' e 'didChange ....' ma l'interfaccia utente non ha mai ricevuto notifica della modifica! (Nota a margine: minA è una variabile a sé stante, potrebbe non apparire in questo modo perché ho semplificato il codice per questa domanda) –

+0

In tal caso, c'è qualcos'altro che non va. Non dovresti davvero chiamare "willChange ..." e "doChange ..." in un setter del genere. Per quanto riguarda 'minA' essere una variabile a sé stante, capito, e in tal caso il mio suggerimento non funzionerà. Tuttavia, forse dovresti pubblicare il codice completo, nel caso in cui la tua semplificazione cambi o rimuova il problema. –

2

L'implementazione di un setter manuale non implica necessariamente la necessità di implementare le notifiche KVO manuali. Ne hai assolutamente bisogno solo se stai aggiornando il supporto di una proprietà senza passare per il setter. KVO sostituirà automaticamente i tuoi setter con versioni che chiamano i metodi will/didChange prima e dopo che il setter "reale" viene chiamato. Se vuoi chiamarli tu stesso nel tuo setter (ad esempio hai bisogno di un controllo più preciso su quando vengono chiamati) devi sostituire il metodo +automaticallyNotifiesObserversForKey: per restituire NO per la tua chiave type.

Suppongo che il motivo per cui si sta ottenendo un errore in questo momento è perché il macchinario KVO vede che avete chiamato [self willChangeValueForKey:@"type"] due volte di fila (un tempo via "magia" KVO, una volta manualmente) senza chiamare [self didChangeValueForKey:@"type"].

0

come altri hanno notato, è possibile abbandonare i willChangeValueForKey:, didChangeValueForKey: chiamate e mettere in atto:

+ (NSSet *) keyPathsForValuesAffectingMinA 
{ 
    return [NSSet setWithObject:@"type"]; 
} 

Lo stesso per minB , ovviamente.

Quindi fare minA e minB proprietà di sola lettura.

I diversi modi per farlo sono in gran parte le preferenze stilistiche, però.

Problemi correlati