KVO底層原理

首先認(rèn)識一個概念:

響應(yīng)式編程: 簡稱RP(Reactive Programming)
響應(yīng)式編程是一種面向數(shù)據(jù)流和變化傳播的編程范式。這意味著可以在編程語言中很方便地表達靜態(tài)或動態(tài)的數(shù)據(jù)流,而相關(guān)的計算模型會自動將變化的值通過數(shù)據(jù)流進行傳播。
OC中的通知、代理、KVO、事件(addTarget)同屬于響應(yīng)式,添加觀察者,設(shè)置代理,添加Target類似于響應(yīng)式編程中的 “訂閱”

KVO

_p = [[Person alloc]init];
// 添加觀察者,當(dāng)name屬性值發(fā)生變化就會通知self去響應(yīng)
[_p addObserver:self forKeyPath:@”name” options:NSKeyValueObservingOptionNew context:nil];

// 響應(yīng)觀察
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{

}

原理: OC的屬性是成員變量 _name 跟 getter 、setter 的封裝。

改變成員變量的值 p->_name = @"xiaowu"; 不會響應(yīng)方法,
所以KVO內(nèi)部(不只是)是觀察setter方法。

底層原理:①利用 runtime 動態(tài)創(chuàng)建一個 Person 子類 NSKVONotifing_Person重寫 setName 方法

-(void)setName:(NSString*)name{
    [self willChangeValueForKey:@”name”];
    [super setName:name];
    [self didChangeValueForKey:@”name”];
    /* 以上兩個change方法會觸發(fā)響應(yīng),options參數(shù)傳  
        NSKeyValueObservingOptionNew新值則會在改變之后  
        didChange觸發(fā),old在改變之前willChange觸發(fā)
    */
}

② 動態(tài)改變 P 對象的類型(一個對象的真實類型,調(diào)用方法看 isa 指針,其內(nèi)部是改變isa指針?biāo)赶虻念悾?,變?yōu)樽宇愵愋?,所以?dāng)用 setter 改變屬性值時 _P.name=@”xiaowu”; 會調(diào)用子類重寫的 setName 方法,進行響應(yīng)

img.png

KVO分為兩種觸發(fā)模式:自動觸發(fā)(一改變值就自動觸發(fā)響應(yīng));手動觸發(fā)(變化不一定每次都通知,有時候需要滿足一定條件之后再通知)

在被觀察的 Person.m 文件里面添加類方法:

+(BOOL)automaticallyNotifiesObserversForKey:(NSString *)key{
    if([key isEqualToString:@”name”]){
        return NO;
    }
    return YES;// YES自動觸發(fā),NO為手動觸發(fā)
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [self willChangeValueForKey:@"name"];
    _p.name = @"xiaowu";
    [self didChangeValueForKey:@"name"];//當(dāng)改變后就會手動觸發(fā)
}

KVO中屬性的依賴關(guān)系:dog_p 的屬性,dog 中有 agelevel 屬性,可直接通過 keyPath:dog.age 觀察 age 的改變;

當(dāng)我們要觀察 dog 中的 age 、level 屬性時得 addObserve 兩次,比較麻煩,可以為其添加依賴關(guān)系,只需觀察 dog ,然后在 Person 類中實現(xiàn)類方法:

+(NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key{
    NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
    if ([key isEqualToString:@"dog"]) {
        // 屬性依賴關(guān)系里面的屬性名稱一定要有下劃線成員變量
        NSArray *affectingKeys = @[@"_dog.age",@"_dog.level"];
        keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
    }
    return keyPaths;
}

KVO觀察不到集合屬性的變化?(如 _p 的數(shù)組屬性 arr ,往 arraddObject 添加元素時不會響應(yīng)),因為KVO是監(jiān)聽 setter 方法,所以不會響應(yīng)。
可以借助KVC:

//根據(jù) *\_p* 對象的 *arr* 屬性返回一個新的數(shù)組
NSMutableArray*tmpArr = [_p mutableArrayValueForKey:@"arr"];
[tmpArr addObject:@”11”];

打印響應(yīng)的字典 change 得到

img1.png

kind 有四種,如下圖:

img2.png

默認(rèn)為1,監(jiān)聽setter,2為插入,3為刪除,4為替換;2、3、4都為容器類型的屬性監(jiān)聽,其內(nèi)部原理跟setter一樣,也是動態(tài)的生成容器的子類,重寫addObject、remove、replace等方法,改變isa指針指向子類。

函數(shù)式編程:如AFN的Block設(shè)計,響應(yīng)式中混合函數(shù)式,回調(diào)在一塊block中,相比代理、通知等業(yè)務(wù)邏輯更加緊密。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容