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"
)