2012-02-13 25 views
6

Các lớp mô hình của tôi chủ yếu được thực hiện với các phương thức setter/getter tổng hợp và mọi thứ đều ổn. Tất cả mọi thứ đã được nối độc đáo với giao diện người dùng. Sau đó tôi nhận ra rằng việc thay đổi một thuộc tính sẽ khiến các thuộc tính khác thay đổi (thay đổi type có thể gây ra các thay đổi trong minAmaxA) vì vậy tôi đã viết các phương thức setter/getter theo cách thủ công cho thuộc tính type. Mã sau:Tại sao tôi có ngoại lệ không bị bắt buộc khi triển khai các phương thức setter/getter KVC của riêng mình

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 

HỎI: Kể từ đó, bất cứ khi nào tôi thay đổi loại của một phần tử tôi nhận được một ngoại lệ uncaught:

Không thể cập nhật cho người quan sát < NSTableBinder ...> cho con đường quan trọng "type.minRange" từ < QBElement ...>, rất có thể vì giá trị cho phím "loại" đã được thay đổi mà không một KVO thích hợp thông báo đang được gửi. Kiểm tra sự tuân thủ KVO của lớp QBElement.

Có điều gì hiển nhiên tôi bị thiếu trong số KVO-Compliance không? Tại sao tôi nhận được lỗi này?

Trả lời

3

Bạn không cần gọi rõ ràng -willChangeValueForKey:-didChangeValueForKey: vì trình đặt tên của bạn được đặt tên chính xác. Các cuộc gọi đến họ sẽ được thêm tự động thông qua sự kỳ diệu của máy móc của KVC/KVO. Vấn đề có thể là chúng được gọi hai lần một cách hiệu quả.

Ngoài ra, kể từ khi nó xuất hiện rằng minAmaxA chỉ đơn giản là xuất phát từ loại, bạn có thể làm cho họ chỉ đọc và nói KVO để tự động thông báo cho các quan sát viên mà Mina và MAXA đã thay đổi bất cứ lúc nào type thay đổi:

@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

Tôi đã thử chạy ứng dụng mà không có 'willChangeValueForKey:' và 'didChange ....' nhưng giao diện người dùng không bao giờ được thông báo về thay đổi! (Lưu ý phụ: minA là một biến riêng của nó, nó có thể không xuất hiện theo cách này bởi vì tôi đã đơn giản hóa mã cho câu hỏi này) –

+0

Trong trường hợp đó, có điều gì khác sai. Bạn thực sự không cần phải gọi 'willChange ...' và 'didChange ...' trong một setter như thế. Đối với 'minA' là một biến riêng của nó, được hiểu, và trong trường hợp đó đề xuất của tôi sẽ không hoạt động. Tuy nhiên, có lẽ bạn nên đăng mã đầy đủ, trong trường hợp đơn giản hóa của bạn thay đổi hoặc xóa sự cố. –

2

Triển khai trình đặt thủ công không có nghĩa là bạn nhất thiết cần triển khai thông báo KVO thủ công. Bạn chỉ cần hoàn toàn nếu bạn đang cập nhật ivar sao lưu cho một thuộc tính mà không phải trải qua setter. KVO sẽ tự động thay thế các setters của bạn bằng các phiên bản gọi các phương thức will/didChange trước và sau khi setter "thực" được gọi. Nếu bạn muốn tự gọi chúng trong setter của mình (ví dụ: bạn cần kiểm soát chính xác hơn khi được gọi), bạn cần ghi đè phương thức +automaticallyNotifiesObserversForKey: để trả lại NO cho khóa type của mình.

Tôi giả định lý do tại sao bạn gặp lỗi ngay bây giờ là do máy móc KVO thấy rằng bạn đã gọi [self willChangeValueForKey:@"type"] hai lần liên tiếp (một lần qua KVO "ma thuật", một lần thủ công) mà không cần gọi [self didChangeValueForKey:@"type"].

0

như những người khác đã lưu ý, bạn có thể bỏ đi điều willChangeValueForKey:, didChangeValueForKey: cuộc gọi và thực hiện:

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

Tương tự cho minB , tất nhiên.

Sau đó, hãy thực hiện các thuộc tính chỉ đọc minAminB.

Tuy nhiên, các cách khác nhau để làm điều đó chủ yếu là các sở thích về phong cách.

Các vấn đề liên quan