「这是我参与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
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
评论(0)