首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C KVO (鍵值觀察) 完整指南:原理、實現與進階技巧

     編輯:關於C語言

KVO 核心機制

KVO (Key-Value Observing) 允許對象監聽另一對象屬性變化。當被觀察屬性修改時,觀察者收到回調 observeValueForKeyPath:ofObject:change:context:。

三大前提條件

  1. 被觀察對象屬性需 KVC 合規 (有 getter/setter,或實例變量 _property)
  2. 觀察者實現 observeValueForKeyPath:ofObject:change:context:
  3. 調用 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

Options 選項詳解

選項change 字典鍵用途
NSKeyValueObservingOptionNewNSKeyValueChangeNewKey獲取新值
NSKeyValueObservingOptionOldNSKeyValueChangeOldKey獲取舊值
NSKeyValueObservingOptionInitial首次註冊即觸發初始化同步
NSKeyValueObservingOptionPriorNSKeyValueChangeNotificationIsPriorKey變更前/後通知

Change 字典完整鍵值

  • NSKeyValueChangeKindKey:變更類型 (Setting/Insertion/Removal/Replacement)
  • NSKeyValueChangeNewKey / OldKey:新/舊值
  • NSKeyValueChangeIndexesKey:集合變更的索引集
  • NSKeyValueChangeNotificationIsPriorKey:是否為 Prior 通知

手動觸發通知 (繞過自動 KVO)

重寫 +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;  // 真實值
}

關鍵注意事項

1. 必須移除觀察者
在 dealloc 中調用 removeObserver:forKeyPath:,否則被觀察者發送通知給已釋放對象 → Crash。
2. 觀察者可多對一
多個觀察者可觀察同一鍵路徑,形成通知鏈。順序不保證。
3. KVO 內部實現
Runtime 動態生成子類 (NSKVONotifying_OriginalClass),重寫 setter 注入通知邏輯。isa 指針修改,故不可用 isa 判斷類型。
4. 非 KVC 合規屬性不可觀察
readonly、無 setter、非對象類型 (需封裝) 等。

KVO vs NotificationCenter vs Delegate

機制耦合度適用場景性能
KVO低 (僅需鍵路徑)屬性變化監聽、UI 數據綁定中 (Runtime 動態派發)
NotificationCenter極低 (字符串名)跨模塊廣播、系統事件低 (同步廣播)
Delegate中 (協議)一對一回調、業務邏輯委託高 (直接調用)

總結

KVO 是 Cocoa 綁定技術基石,核心價值在於層級解耦:觀察者無需知曉被觀察者內部實現,僅依賴鍵路徑。掌握自動/手動通知切換、正確移除註冊、理解 Runtime 實現原理,是高階 iOS/macOS 開發必備技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved