Почему я получаю необработанное исключение при реализации моих собственных методов установки / получения KVC - PullRequest
6 голосов
/ 14 февраля 2012

Мои классы моделей в основном реализованы с помощью синтезированных методов установки / получения, и все было хорошо. Все было приятно подключено к пользовательскому интерфейсу. Позже я понял, что изменение одного свойства должно привести к изменению других свойств (изменение type может вызвать изменения в minA и maxA), поэтому я вручную написал методы setter / getter для свойства type. Код следует:

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

ВОПРОС : С тех пор всякий раз, когда я меняю Тип элемента, я получаю необработанное исключение :

Невозможно обновить для наблюдателя для ключевого пути "type.minRange" из , скорее всего потому, что значение для ключ "тип" был изменен без соответствующего уведомления KVO послан. Проверьте KVO-соответствие класса QBElement.

Есть ли что-то очевидное, чего мне не хватает в KVO-Compliance ? Почему я получаю эту ошибку?

Ответы [ 3 ]

3 голосов
/ 14 февраля 2012

Вам не нужно явно вызывать -willChangeValueForKey: и -didChangeValueForKey:, потому что ваш установщик назван правильно. Звонки к ним будут добавлены автоматически через магию машин KVC / KVO Проблема может заключаться в том, что их фактически вызывают дважды.

Кроме того, поскольку кажется, что minA и maxA просто получены из типа, вы можете сделать их доступными только для чтения и указать KVO автоматически уведомлять наблюдателей о том, что minA и maxA меняются в любое время type изменения:

@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
2 голосов
/ 14 февраля 2012

Внедрение ручного установщика не означает, что вам обязательно нужно реализовать ручные уведомления KVO.Они вам просто необходимы, если вы обновляете резервный ivar для свойства, не обращаясь к установщику.KVO автоматически заменит ваши сеттеры версиями, которые вызывают методы will / didChange до и после вызова «настоящего» сеттера.Если вы хотите вызывать их самостоятельно в своем установщике (например, вам нужно более точное управление при вызове), вам нужно переопределить метод +automaticallyNotifiesObserversForKey:, чтобы вернуть NO для вашей клавиши type.

Я предполагаю, что причина, по которой вы получаете ошибку прямо сейчас, заключается в том, что механизм KVO видит, что вы вызывали [self willChangeValueForKey:@"type"] два раза подряд (один раз с помощью "волшебства" KVO, один раз вручную) беззвонить [self didChangeValueForKey:@"type"].

0 голосов
/ 14 февраля 2012

Как уже отмечали другие, вы можете отказаться от вызовов willChangeValueForKey:, didChangeValueForKey: и реализовать:

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

То же самое для minB, конечно.

Затем сделать minA и minB свойства только для чтения.

Однако разные способы сделать это во многом стилистические предпочтения.

...