「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。
KVO
KVO,全称Key-Value observing,即键值观察,Apple官方文档,它可以将其他对象指定属性的更改通知给观察者,在iOS开发中,经常使用kvo监工行2.5亿存款不翼而飞听属性的变化,并做出响应(例如UI刷新等)。
测试测试你的自卑程度Demo地址
特点
- 一对多
- 只能监听对象属性的变化
- 通过NSString查找,数组公式编写时不会查错补测试用例全
- 发送通apple id密码重置知由系统控制
- 可以记录新旧值得变化
使用
一 注册观察者(addObse宫颈癌rver:forKeyPath:options:context)
- self.person 被观察对象
- observ数组公式er 响应对象
- keyPath 观察的属性
- options 定义观察选项,下面回详细讲
- context 个人理指针式万用表解一个标记,用来区分在回调里区分通知数组c语言来源,不使用则NULL
static void *NameContext = &NameContext;
self.person = CQPerson.new;
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:NameContext];
[self.person addObserver:self forKeyPath:@"nickName" options:NSKeyValueObservingOptionNew context:NULL];
二 在addObserver:forKeyPath:options:context函数监听观察通知回调
- keyPath 属性
- object 对象数组词
- change 新旧值集合,和注册的options有关
- context 可根据context直接区分,例如不同对象的同名属性,当然用obj配合keyPath也可以
// object
- (void)observeValueForKeyPath:(**NSString** *)keyPath ofObject:(id)object change:(**NSDictionary**<NSKeyValueChangeKey,id> *)change context:(void *)context {
if (context == NameContext) {
}
NSLog(@"对象-%@,属性-%@",object, keyPath);
}
三 移除观测试工程师察者(removeObserver:forKeyPath:context)
- 必须注册了才能移除,否则NS宫颈癌疫苗RangeException崩溃,可使用try
- 注册和移除成对出现,如果不移除,释放后依然会有通知,可能导致野指针崩溃
- 典型使用,在init指针或viewDidLoad中注指针数组册为观察者,在dealloc 中移除
[self.person removeObserver:self forKeyPath:@"name" context:NULL];
[self.person removeObserver:self forKeyPath:@"nickName" context:NULL];
自动触发和手动触发
- 默认为自动触发,通过重写automaticallyNotifiesObserversForKey可关闭
+ (BOOL)automaticallyNotifiesObserversForKey:(**NSString** *)key {
return NO;
}
- 如果属性不可见或只读,通枸杞过公开函数间接调用私有set函数,可以触发
- 如果属性不可见或只读,通过performSelector或IMP调指针数组用set函数,均可触发
- 通过kvc修改属性名,可以触发
- 在以上基础上,赋相同的值,也会触发,部分场景下,注意检查是否相同
// 通过公开函数间接调用私有set函数,可以监听到*
[self.person reloadName];
// 通过performSelector 调用私有set函数,可以监听到*
[self.person performSelector:@selector(setName:) withObject:@"1"];
// 通过IMP 调用私有set函数,可以监听到*
SEL sel = NSSelectorFromString(@"setName:");
IMP imp = [self.person methodForSelector:sel];
void(*func)(id, SEL, **NSString** *) = (void *)imp;
func(self.person, sel, @"1");
// 简写
((void(*)(id, SEL, **NSString** *))[self.person methodForSelector:sel])(self.person, sel, @"1");
// 通过kvc直接修改属性名,会触发KVO
[self.person setValue:@"1" forKey:@"nickName"];
- 通过指针万用表怎么读数kvc直接修改私有成员变量,不会触发KVO
- 通过公开函数间接修改私有成员变量,也不会触发KVO
- 总之,对成员变量修改不会触发,只有属性的修改才能触发
// 通过kvc直接修改私有成员变量,不会触发KVO
[self.person setValue:@"100" forKey:@"_nickName"];
- 关闭自动龚俊触发后,可手动触发
- willChangeValueForKey 修改前触发
- didChangeValueForKey 修改后触发
- options为Old和New时两个函数必须全部调用才能触发
- options为Prior时,只调用willChangeValueForKey可触发修改前的监听,必须两个都实现才能触发修改后的监听
- (void)setName:(**NSString** * _Nonnull)name {
[self willChangeValueForKey:@"name"];
_name = name;
[self didChangeValueForKey:@"name"];
}
合并触发
例数组指针如C属性依赖A和B,只要工行2.5亿存款不翼而飞AB有一个变C就会变,可以重写keyPath指针数组sFo测试你适合学心理学吗rValuesAffectingValueForKey函数
+ NSSet<NSString *> *)keyPathsForValuesAffectingValueForKey:(NSString *)key {
NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key];
if ([key isEqualToString:@"mergeName"]) {
NSArray *affectingKeys = @[@"name", @"nickName"];
keyPaths = [keyPaths setByAddingObjectsFromArray:affectingKeys];
}
return keyPaths;
}
- 当然,在AB的set函数里调用C的set也是OK的
观察容器类
- 对容google器类添加数据是不会调用set函数的,所以也触发不了kvo
- 需要使用对应的函数,例如数组mutableArrayValueForKey
[self.person.testArr addObject:@"1"]; // 不会触发
[[self.person mutableArrayValueForKey:@"testArr"] addObject:@"1"]; // 可以触发
# NSKeyValueObservingOptio工行2.5亿存款不翼而飞ns
一共有四种,可同时使用,分别是
-
NSKeyValueOb数组长度serv数组c语言ingOptionNew:在回调chang指针数组e里提供更改后的新值(key-@”new”),值被修改后触发回调
-
NSKeyValueObservingOptioapple watchnOld:在回调change提供更改前的值(key-@”old”),值被修改后触发回调
-
NSKe工商银行yValueObservingOptionInitial:观察最初的值(Apple在注册观察服务时会调用一次触发方法)
-
NSKeyValueO数组指针bservingOptio指针和引用的区别nPrior:分别在值修改前后触发方法测试抑郁症的20道题(即一次修改有两次触发)在回调change里Apple有key@”notification数组词IsPrior”
-
注意如果使用了手动触发
-
- options为Old和New时willChangeValueForKey/didChangeValueFor测试你适合学心理学吗Key必须全部调用才能触发
-
- options为Prior时,只调用willChangeV指针万用表的使用方法alueForKey可触发修改前的监听,必须两个都实现才能触发修改后的监听
-
四种Option可同时使用
原理解析
- 保证自动触发没有关闭
- 注册前后打印isa指针的指向,可以发现注册后指向了派生类NSKVONotifying_CQPerson
(lldb) po object_getClassName(self.person)**
"CQPerson"
(lldb) po object_getClassName(self.person)**
"NSKVONotifying_CQPerson"
- 继续探究测试用例中间类,发现中间类重写了观察属性的setter方法、class、dealloc、_isKVOA方法,隐藏对象真实类信息
- 重写dealloc做了一些 KVO 内存释放
- 在setter方法内部调用了Foundation 的 _NSS测试手机是否被监控etObjectValu数组去重方法eAndNotify 函数
-
- a) 首先会调用 w数组c语言illChangeValueForKey
-
- b) 然后给属性赋值
-
- c) 最后调用 didChangeValueForKey
-
- d) 最后调用 observer 的 observeValueForKeyPath 去告诉监听器属性值发生了改变 .app store
-
- (void)printClass:(Class)cls {
// 注册类的总数*
int count = objc_getClassList(NULL, 0);
NSMutableArray *mArray = [**NSMutableArray array];
// 获取所有已注册的类
Class *classes = (Class *)malloc(sizeof(Class)*count);
objc_getClassList(classes, count);
for (int i = 0; i<count; i++) {
if (cls == class_getSuperclass(classes[i])) {
[mArray addObject:classes[i]];
[self printClassAllMethod:cls];
}
}
free(classes);
NSLog(@"class = %@", mArray);
}
- (void)printClassAllMethod:(Class)cls{
unsigned int count = 0;
Method *methodList = class_copyMethodList(cls, &count);
for (int i = 0; i<count; i++) {
Method method = methodList[i];
SEL sel = method_getName(method);
IMP imp = class_getMethodImplementation(cls, sel);
NSLog(@"%@-%p",NSStringFromSelector(sel),imp);
}
free(methodList);
}
- 移除KVO观察者之后,实例对象isa指向由
中间类
更改为原有类
-
中间类
从创建后,就一测试用例直存在内存中,不会被销毁
自定义KVO
- 跟系统kvo流程基本一致,做一些优化处理,例如block回调
- 大致步骤
-
- 注册观察者以及响应
-
- 1、验证
set
函数是否存在
- 1、验证
-
- 2、保存信息
-
- 3、动态生成子类,重写
class
、setter
方法
- 3、动态生成子类,重写
-
- 4、在子类的set函数中向父类发消息,即自定义消息发送
-
- 5、指针式万用表让观察者响应
-
- 移除观察者
- 1、更工行2.5亿存款不翼而飞改
isa指向
为原有类 - 2、重写子类的
dealloc
方法
参考facebookarchiv测试抑郁程度的问卷e/KVOControllerapple watch