2012-02-13 31 views
6

Mis clases de modelo se implementan principalmente con métodos setter/getter sintetizados y todo estaba bien. Todo estaba muy bien conectado a la interfaz de usuario. Más tarde me di cuenta de que cambiar una propiedad debería ocasionar el cambio de otras propiedades (cambiar type podría causar cambios en minA y maxA) así que escribí manualmente los métodos setter/getter para la propiedad type. Código sigue:¿Por qué recibo una excepción no detectada al implementar mis propios métodos KVC setter/getter

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 

PREGUNTA: Desde entonces, cada vez que cambiar el tipo de un elemento consigo un excepción no detectada:

No se puede actualizar para el observador < NSTableBinder ...> para la ruta de la clave "type.minRange" de < QBElement ...>, muy probablemente debido a que el valor de el "tipo" llave fue cambiado sin una adecuada MVA notificación siendo enviada. Verifique KVO-Compliance de la clase QBElement.

¿Hay algo obvio que me falta de KVO-Compliance? ¿Por qué obtengo este error?

Respuesta

3

No necesita llamar explícitamente a -willChangeValueForKey: y -didChangeValueForKey: porque su setter tiene el nombre correcto. Las llamadas se agregarán automáticamente a través de la magia de la maquinaria de KVC/KVO. El problema puede ser que efectivamente se los llame dos veces.

También, ya que parece que minA y maxA se derivan simplemente de la clase, puede hacerlos de sólo lectura y contar MVA para notificar automáticamente a los observadores que Mina y MAXA han cambiado cualquier momento type cambios:

@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

He intentado ejecutar la aplicación sin 'willChangeValueForKey:' y 'didChange ....' pero la interfaz de usuario nunca ha sido notificada del cambio. (Nota al margen: minA es una variable en sí misma, podría no aparecer de esta manera porque simplifiqué el código para esta pregunta) –

+0

En ese caso, hay algo más que está mal. Realmente no debería necesitar llamar 'willChange ...' y 'didChange ...' en un setter como ese. En cuanto a 'minA' es una variable en sí misma, entendida, y en ese caso mi sugerencia no funcionará. Sin embargo, quizás deba publicar el código completo, en caso de que su simplificación cambie o elimine el problema. –

2

La implementación de un ajustador manual no significa necesariamente que necesite implementar notificaciones KVO manuales. Solo los necesitas si estás actualizando el respaldo de una propiedad sin pasar por el sistema. KVO reemplazará automáticamente sus setters con versiones que invoquen los métodos will/didChange antes y después de llamar al "real" setter. Si desea llamarlos usted mismo en su incubadora (por ejemplo, necesita un control más preciso sobre cuándo se llaman), debe anular el método +automaticallyNotifiesObserversForKey: para devolver NO para su clave type.

Supongo que la razón por la que está recibiendo un error en este momento es porque la maquinaria KVO ve que ha llamado [self willChangeValueForKey:@"type"] dos veces seguidas (una vez a través de KVO "magia", una vez manualmente) sin llamar al [self didChangeValueForKey:@"type"].

0

Como otros han señalado, puede deshacerse de los willChangeValueForKey:, didChangeValueForKey: llamadas y poner en práctica:

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

Lo mismo para minB , por supuesto.

Luego haga minA y minB propiedades de solo lectura.

Sin embargo, las diferentes formas de hacerlo son preferencias en gran parte estilísticas.

Cuestiones relacionadas