Tôi hy vọng lời giải thích này không phải là quá trừu tượng để hiểu.
Giả sử bạn tạo một lớp MyViewController
, là lớp con của UIViewController
. Bạn không có mã nguồn của UIViewController
.
Bây giờ bạn quyết định thực hiện MyViewController
sử dụng KVO để quan sát các thay đổi đối với thuộc tính center
của self.view
. Vì vậy, bạn hợp lệ thêm mình là một người quan sát:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addObserver:self forKeyPath:@"center" options:0 context:NULL];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.view removeObserver:self forKeyPath:@"center"];
[super viewDidDisappear:animated];
}
Vấn đề ở đây là bạn không biết nếu UIViewController
cũng đăng ký chính nó như là một quan sát viên của self.view
's center
. Nếu có, bạn có thể có hai vấn đề:
- Bạn có thể được gọi hai lần khi trung tâm của chế độ xem thay đổi.
- Khi bạn tự xóa mình với tư cách người quan sát, bạn cũng có thể xóa đăng ký KVO
UIViewController
.
Bạn cần một cách để đăng ký chính mình với tư cách là người quan sát có thể phân biệt với đăng ký KVO của UIViewController
. Đó là nơi mà đối số context
xuất hiện. Bạn cần phải chuyển một giá trị cho context
mà bạn hoàn toàn chắc chắn UIViewController
là không phải là sử dụng làm đối số context
. Khi bạn hủy đăng ký, bạn lại sử dụng cùng một số context
để bạn chỉ xóa đăng ký của mình, chứ không phải đăng ký của UIViewController
. Và trong phương thức observeValueForKeyPath:ofObject:change:context:
của bạn, bạn cần phải kiểm tra số context
để xem thư đó là dành cho bạn hay cho siêu lớp của bạn.
Một cách để đảm bảo bạn sử dụng số context
mà không có gì khác sử dụng là tạo biến số static
trong MyViewController.m
. Sử dụng nó khi bạn đăng ký và hủy đăng ký, như thế này:
static int kCenterContext;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.view addObserver:self forKeyPath:@"center" options:0 context:&kCenterContext];
}
- (void)viewDidDisappear:(BOOL)animated {
[self.view removeObserver:self forKeyPath:@"center" context:&kCenterContext];
[super viewDidDisappear:animated];
}
Sau đó, trong phương pháp observeValueForKeyPath:...
của bạn, kiểm tra xem nó như thế này:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == &kCenterContext) {
// This message is for me. Handle it.
[self viewCenterDidChange];
// Do not pass it on to super!
} else {
// This message is not for me; pass it on to super.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
Bây giờ bạn sẽ được bảo đảm không ảnh hưởng đến việc KVO của lớp cha của bạn, nếu nó làm bất kỳ. Và nếu ai đó làm cho một lớp con của MyViewController
cũng sử dụng KVO, nó sẽ không can thiệp vào KVO của bạn.
Cũng lưu ý rằng bạn có thể sử dụng ngữ cảnh khác nhau cho mỗi đường dẫn chính mà bạn quan sát. Sau đó, khi hệ thống thông báo cho bạn về một thay đổi, bạn có thể kiểm tra ngữ cảnh thay vì kiểm tra đường dẫn chính. Kiểm tra tính bình đẳng của con trỏ nhanh hơn một chút so với việc kiểm tra tính bình đẳng của chuỗi.Ví dụ:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
change:(NSDictionary *)change context:(void *)context
{
if (context == &kCenterContext) {
[self viewCenterDidChange];
// Do not pass it on to super!
} else if (context == &kBackgroundColorContext) {
[self viewBackgroundDidChange];
// Do not pass it on to super!
} else if (context == &kAlphaContext) {
[self viewAlphaDidChange];
// Do not pass it on to super!
} else {
// This message is not for me; pass it on to super.
[super observeValueForKeyPath:keyPath ofObject:object
change:change context:context];
}
}
thể trùng lặp của [Các thông số từ observeValueForKeyPath: ofObject: thay đổi: bối cảnh:] (http://stackoverflow.com/questions/1625575/parameters-from-observevalueforkeypathofobjectchangecontext) –