KVO的底層原理

KVO的全稱是Key-Value Observing关摇,俗稱“鍵值監(jiān)聽”,可以用于監(jiān)聽某個對象屬性值的改變

基本使用方法

//
//  WKPerson.h
//  lllmmnn
//
//  Created by wukai on 2019/1/4.
//  Copyright ? 2019年 諸葛小亮. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface WKPerson : NSObject

@property (nonatomic, assign) NSInteger age;

@end

//
//  WKPerson.m
//  lllmmnn
//
//  Created by wukai on 2019/1/4.
//  Copyright ? 2019年 諸葛小亮. All rights reserved.
//

#import "WKPerson.h"

@implementation WKPerson

- (void)setAge:(NSInteger)age{
    
    _age = age;
}

@end

self.person1 = [[WKPerson alloc] init];
self.person1.age = 1;
    
//content 代表文本 現(xiàn)在監(jiān)聽
[person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
 person1.age = 10;

//建值更改后 會自動進(jìn)入 conten傳入123 這里的content就是123
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    
    NSLog(@"%@",change);
    
}

//移除
- (void)dealloc{
    
    [self.person removeObserver:self forKeyPath:@"age"];
}

  • 為啥更改了鍵值之后就可以立馬收到通知
  • 我們來深入的看看
#import "ViewController.h"

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    self.person1.age = 10;
    NSLog(@"更改之后");
}

@end
#import "WKPerson.h"

@implementation WKPerson

- (void)setAge:(NSInteger)age{
    
    _age = age;
}

@end

2019-01-05 11:43:36.779705+0800 lllmmnn[3027:195001] {
    kind = 1;
    new = 10;
    old = 1;
}
2019-01-05 11:46:38.422867+0800 lllmmnn[3027:195001] 更改之后

結(jié)論是 執(zhí)行完的結(jié)果是 set里面之后,立即通知發(fā)生更改身害,我們來看看更改了什么

 self.person1 = [[WKPerson alloc] init];
 self.person1.age = 1;
    
self.person2 = [[WKPerson alloc] init];
self.person2.age = 2;

NSLog(@"KVO之前 person1 = %@",object_getClass(_person1));
NSLog(@"KVO之前 person2 = %@",object_getClass(_person2) );

[_person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];

NSLog(@"KVO之后 person1 = %@",object_getClass(_person1));
NSLog(@"KVO之后 person2 = %@",object_getClass(_person2));
NSLog(@"KVO之后的父類 person2 = %@",[_person2 superclass]);
NSLog(@"KVO之后的父類 person1 = %@",[object_getClass(_person1) superclass]);

2019-01-05 11:43:24.794017+0800 lllmmnn[3027:195001] KVO之前 person1 = WKPerson
2019-01-05 11:43:24.794119+0800 lllmmnn[3027:195001] KVO之前 person2 = WKPerson
2019-01-05 11:43:33.617426+0800 lllmmnn[3027:195001] KVO之后 person1 = NSKVONotifying_WKPerson
2019-01-05 11:43:34.346046+0800 lllmmnn[3027:195001] KVO之后 person2 = WKPerson
2019-01-05 11:54:37.120260+0800 lllmmnn[3181:214328] KVO之后的父類 person2 = NSObject
2019-01-05 11:54:37.120365+0800 lllmmnn[3181:214328] KVO之后的父類 person1 = WKPerson


由此可以說明 當(dāng)KVO鍵值監(jiān)聽之后狞贱,系統(tǒng)會通過runtime生成一個衍生類NSKVONotifying_WKPerson 繼承于WKPerson

接下來我們來看看 setAge里面的實現(xiàn)

   
    NSLog(@"KVO之前person1=%p",[_person1 methodForSelector:@selector(setAge:)]);
    NSLog(@"KVO之前person2=%p",[_person2 methodForSelector:@selector(setAge:)]);
   
  [_person1 addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
    NSLog(@"KVO之后person1=%p",[_person1 methodForSelector:@selector(setAge:)]);
    NSLog(@"KVO之后person2=%p",[_person2 methodForSelector:@selector(setAge:)]);
   

2019-01-05 13:52:52.379255+0800 lllmmnn[4611:339139] KVO之前person1=0x101dcc430
2019-01-05 13:52:52.379450+0800 lllmmnn[4611:339139] KVO之前person2=0x101dcc430
2019-01-05 13:52:54.873704+0800 lllmmnn[4611:339139] KVO之后person1=0x102112bf4
2019-01-05 13:52:54.873833+0800 lllmmnn[4611:339139] KVO之后person2=0x101dcc430

我們發(fā)現(xiàn)KVO之后person1的內(nèi)存地址發(fā)生了變化
(lldb) p (IMP)0x101dcc430
(IMP) $0 = 0x0000000101dcc430 (lllmmnn`-[WKPerson setAge:] at WKPerson.m:13)
(lldb) p (IMP)0x102112bf4
(IMP) $1 = 0x0000000102112bf4 (Foundation`_NSSetLongLongValueAndNotify)

  • LLDB調(diào)試之后我們發(fā)現(xiàn)沒有KVO的setAge方法 內(nèi)部實現(xiàn)就是[WKPerson setAge:]
  • 調(diào)用了KVO的 setAge內(nèi)部實現(xiàn)是調(diào)用了Foundation框架里面的 _NSSetLongLongValueAndNotify 這個方法
  • 由此我們可以得出 KVO 是在setAge方法里面調(diào)用了_NSSetLongLongValueAndNotify這個方法
#pragma mark - 獲取類的所有方法
- (NSArray *)getClassMethodsWithClass:(Class)cls {
    
    NSMutableArray *mutArr = [NSMutableArray array];
    
    unsigned int outCount;
    
    /** 第一個參數(shù):要獲取哪個類的方法
     * 第二個參數(shù):獲取到該類的方法的數(shù)量
     */
    Method *methodList = class_copyMethodList(cls, &outCount);
    
    // 遍歷所有的方法
    for (int i = 0; i<outCount; i++) {
        SEL name = method_getName(methodList[i]);
        [mutArr addObject:NSStringFromSelector(name)];
    }
    return [NSArray arrayWithArray:mutArr];
}

  NSArray *arry = [self getClassMethodsWithClass:object_getClass(_person1)];
    NSLog(@"%@",arry);

2019-01-05 13:58:24.512361+0800 lllmmnn[4611:339139] (
    "setAge:",
    class,
    dealloc,
    "_isKVOA"
)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市郎仆,隨后出現(xiàn)的幾起案子只祠,更是在濱河造成了極大的恐慌,老刑警劉巖扰肌,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件抛寝,死亡現(xiàn)場離奇詭異,居然都是意外死亡曙旭,警方通過查閱死者的電腦和手機(jī)盗舰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來桂躏,“玉大人钻趋,你說我怎么就攤上這事〖料埃” “怎么了蛮位?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鳞绕。 經(jīng)常有香客問我失仁,道長,這世上最難降的妖魔是什么们何? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任陶因,我火速辦了婚禮,結(jié)果婚禮上垂蜗,老公的妹妹穿的比我還像新娘楷扬。我一直安慰自己,他們只是感情好贴见,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布烘苹。 她就那樣靜靜地躺著,像睡著了一般片部。 火紅的嫁衣襯著肌膚如雪镣衡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音廊鸥,去河邊找鬼望浩。 笑死,一個胖子當(dāng)著我的面吹牛惰说,可吹牛的內(nèi)容都是我干的磨德。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼吆视,長吁一口氣:“原來是場噩夢啊……” “哼典挑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起啦吧,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤您觉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后授滓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琳水,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年般堆,在試婚紗的時候發(fā)現(xiàn)自己被綠了在孝。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡郁妈,死狀恐怖浑玛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情噩咪,我是刑警寧澤顾彰,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站胃碾,受9級特大地震影響涨享,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仆百,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一厕隧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧俄周,春花似錦吁讨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至波势,卻和暖如春翎朱,著一層夾襖步出監(jiān)牢的瞬間橄维,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工拴曲, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留争舞,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓澈灼,卻偏偏與公主長得像竞川,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蕉汪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容