首先認(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)

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 中有 age 、level 屬性,可直接通過 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 ,往 arr 中 addObject 添加元素時不會響應(yīng)),因為KVO是監(jiān)聽 setter 方法,所以不會響應(yīng)。
可以借助KVC:
//根據(jù) *\_p* 對象的 *arr* 屬性返回一個新的數(shù)組
NSMutableArray*tmpArr = [_p mutableArrayValueForKey:@"arr"];
[tmpArr addObject:@”11”];
打印響應(yīng)的字典 change 得到

kind 有四種,如下圖:

默認(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ù)邏輯更加緊密。