KVO (Key-Value Observing) 允許對象監聽另一對象屬性變化。當被觀察屬性修改時,觀察者收到回調 observeValueForKeyPath:ofObject:change:context:。
observeValueForKeyPath:ofObject:change:context:addObserver:forKeyPath:options:context: 註冊
#import
@interface Son : NSObject
@property (nonatomic, copy) NSString *girlfriendName;
- (void)fallInLoveWith:(NSString*)name;
- (void)think;
@end
@implementation Son
- (void)fallInLoveWith:(NSString*)name { self.girlfriendName = name; }
- (void)think { NSLog(@"My girl is %@...", self.girlfriendName); }
@end
@interface Father : NSObject
@property (nonatomic, strong) Son *son;
@end
@implementation Father
- (instancetype)initWithSon:(Son*)son {
self = [super init];
if (self) {
_son = son;
[son addObserver:self
forKeyPath:@"girlfriendName"
options:NSKeyValueObservingOptionNew
| NSKeyValueObservingOptionOld
| NSKeyValueObservingOptionInitial
context:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString*)keyPath
ofObject:(id)object
change:(NSDictionary*)change
context:(void*)context {
NSString *old = change[NSKeyValueChangeOldKey];
NSString *new = change[NSKeyValueChangeNewKey];
if (!old) { // Initial 選項觸發
NSLog(@"First time knowing son's girlfriend!");
} else {
NSLog(@"Son's %@ changed: %@ → %@", keyPath, old, new);
if ([new isEqualToString:@"Libinbin"]) {
[object fallInLoveWith:@"Fanbinbin"]; // 父親強行修改!
}
}
}
- (void)dealloc {
[self.son removeObserver:self forKeyPath:@"girlfriendName"];
_son = nil;
}
@end
| 選項 | change 字典鍵 | 用途 |
|---|---|---|
| NSKeyValueObservingOptionNew | NSKeyValueChangeNewKey | 獲取新值 |
| NSKeyValueObservingOptionOld | NSKeyValueChangeOldKey | 獲取舊值 |
| NSKeyValueObservingOptionInitial | 首次註冊即觸發 | 初始化同步 |
| NSKeyValueObservingOptionPrior | NSKeyValueChangeNotificationIsPriorKey | 變更前/後通知 |
重寫 +automaticallyNotifiesObserversForKey: 返回 NO,然後在 setter 中手動調用:
+ (BOOL)automaticallyNotifiesObserversForKey:(NSString*)key {
return [key isEqualToString:@"girlfriendName"] ? NO : YES;
}
- (void)setGirlfriendName:(NSString*)name {
[self willChangeValueForKey:@"girlfriendName"];
_girlfriendName = @"女學霸"; // 給父親看的假象
[self didChangeValueForKey:@"girlfriendName"];
_girlfriendName = name; // 真實值
}
removeObserver:forKeyPath:,否則被觀察者發送通知給已釋放對象 → Crash。
| 機制 | 耦合度 | 適用場景 | 性能 |
|---|---|---|---|
| KVO | 低 (僅需鍵路徑) | 屬性變化監聽、UI 數據綁定 | 中 (Runtime 動態派發) |
| NotificationCenter | 極低 (字符串名) | 跨模塊廣播、系統事件 | 低 (同步廣播) |
| Delegate | 中 (協議) | 一對一回調、業務邏輯委託 | 高 (直接調用) |
KVO 是 Cocoa 綁定技術基石,核心價值在於層級解耦:觀察者無需知曉被觀察者內部實現,僅依賴鍵路徑。掌握自動/手動通知切換、正確移除註冊、理解 Runtime 實現原理,是高階 iOS/macOS 開發必備技能。