ios KVO 官方文檔學習

從官方文檔來武裝一下自己(游擊隊->正規軍)

原理說明

When an observer is registered for an attribute of an object the isa pointer of the observed object is modified, pointing to an intermediate class rather than at the true class. As a result the value of the isa pointer does not necessarily reflect the actual class of the instance.

  • 當對一個類添加觀察後,這個類的 isa 指針被指向了一箇中間類,而非真實的了類

關於 KVO 內部實現的原理,官方也就一句話帶過

而這個中間類和 isa 就是 KVO 的核心了

爲什麼要實現一箇中間類呢?

KVO的核心在於,屬性的改變的時候,可以兼容的到

我們一般在更改屬性值的時候一般都是

  1. 點方法 -> setter 方法
  2. setValueForKey
  3. 直接賦值

對於直接賦值,這個屬於直接修改了指針的指向,這個就很難抓到了

所以重點看 setter 方法 和 setValueForKey 這兩個方法

於是 KVO 就要在運行的時候動態的兼容我們的 setter 方法

那麼蘋果的實現方法就是,運行時新建了一個被觀察的對象的子類

將被觀察對象的 isa 指針指向子類

isa 略作說明

每個對象都有 isa 指針,isa 指針存儲了一個類所有的信息。

例如:所有的方法、所有的屬性

換句話來說,我們對 OC 類的操作,底層都是對 isa 指針的操作

例如: isKindOf 方法

其源碼可以看出

對象 -> isa -> isa_class -> isa_super_class 直到找到 isa 類型相等

沒有找到則 return false,找到則 return true

子類重寫了 setter 方法,然後就可以抓到屬性的改變了

至於怎麼通知給觀察者改變的,看下面的 官方文檔重點翻譯

官方文檔重點翻譯

  • OS X 中,model and controller layers 很大程度省依賴 KVO

if your objects inherit from NSObject and you create properties in the usual way, your objects and their properties will automatically be KVO Compliant. It is also possible to implement compliance manually

  • 繼承自 NSObject 的類,常規的方式創建的屬性,都自動的可以使用 KVO

Not all classes are KVO-compliant for all properties

  • 並不是所有的屬性都兼容 KVO

Manual change notification provides additional control over when notifications are emitted, and requires additional coding. You can control automatic notifications for properties of your subclass by implementing the class method [automaticallyNotifiesObserversForKey:](https://developer.apple.com/documentation/objectivec/nsobject/1409370-automaticallynotifiesobservers).

  • 重寫子類的 automaticallyNotifiesObserversForKey: 可以來控制屬性的改變是否發送 KVO 通知
    + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey {
 
    BOOL automatic = NO;
    if ([theKey isEqualToString:@"balance"]) {
        automatic = NO;
    }
    else {
        automatic = [super automaticallyNotifiesObserversForKey:theKey];
    }
    return automatic;
}
  • 觸發 KVO
// Call the accessor method.
[account setName:@"Savings"];
 
// Use setValue:forKey:.
[account setValue:@"Savings" forKey:@"name"];
 
// Use a key path, where 'account' is a kvc-compliant property of 'document'.
[document setValue:@"Savings" forKeyPath:@"account.name"];
 
// Use mutableArrayValueForKey: to retrieve a relationship proxy object.
Transaction *newTransaction = <#Create a new transaction for the account#>;
NSMutableArray *transactions = [account mutableArrayValueForKey:@"transactions"];
[transactions addObject:newTransaction];
  • 重寫 setter 方法 發送更改通知
- (void)setBalance:(double)theBalance {
    [self willChangeValueForKey:@"balance"];
    _balance = theBalance;
    [self didChangeValueForKey:@"balance"];
}
- (void)removeTransactionsAtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"transactions"];
 
    // Remove the transaction objects at the specified indexes.
 
    [self didChange:NSKeyValueChangeRemoval
        valuesAtIndexes:indexes forKey:@"transactions"];
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章