您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息
免费发信息
三六零分类信息网 > 南充分类信息网,免费分类信息发布

OC观察者模式之KVO的使用与思考

2019/10/31 0:14:19发布168次查看

转载本文需注明出处:微信公众号eaworld,违者必究。
引言:
无论用哪种语言进行软件开发,我们都会接触到设计模式,个人认为设计模式存在的意义在于:在某些需求下,采用适合的设计模式,使代码结构合理,从而提高代码的可读性、可扩展性、可移植性,此文将要讨论的是oc开发中的一种常用模式之一:观察者模式之kvo。
kvo俗称键值观察(key-value observe),键值观察是当被观察的对象属性发生改变时,会通知到观察对象的一种机制。
目录:
1、kvo的作用
2、kvo的使用方法
3、kvo的实现原理
4、kvo与kvc、代理、通知的区别
5、kvo实现过程中的注意事项
无论用哪种语言进行软件开发,我们都会接触到设计模式,个人认为设计模式存在的意义在于:在某些需求下,采用适合的设计模式,使代码结构合理,从而提高代码的可读性、可扩展性、可移植性,此文将要讨论的是ios开发中的一种常用模式之一:观察者模式之kvo。我们先看下官方文档给的kvo介绍:
翻译过来就是:kvo是运用isa混写技术实现自动观察键值的。isa指针是指向对象的类,本质上是指向类中的方法实现。当一个对象注册观察者时,这个对象的isa指针被修改指向一个中间类。永远不要用isa来判断一个类的继承关系,而是应该用class方法来判断类的实例。
kvo俗称键值观察(key-value observe),键值观察是当被观察的对象属性发生改变时,会通知到观察对象的一种机制。
1.kvo的作用
1、监听带有状态的基础控件,如开关、按钮等;
2、监听字符串的改变,当监听的字符串改变时,来做一些自定义的操作;
3、当数据模型的数据发生改变时,视图组件能动态的更新,及时显示数据模型更新后的数据,比如tableview中数据发生变化进行刷新列表操作,监听 scrollview的contentoffset属性监听页面的滑动.
2.kvo的使用方法
kvo的使用可分为自动监听和手动监听。
1.自动监听
1.1自动监听操作步骤:
(1)添加观察者
(2)在观察者中添加观察键值方法
(3)在dealloc中移除监听
1.2示例代码:
创建两个类modela和modelb,两个类中都添加属性“des”,在控制器中,将b添加为a的观察者。代码如下:
modela中代码:
modelb中代码:
控制器中代码:
控制器中添加观察者的方法调用的是如下的类方法:
- (void)addobserver:(nsobject *)observer forkeypath:(nsstring *)keypath options:(nskeyvalueobservingoptions)options context:(nullable void *)context
各个参数说明:
@param observer 被监听的对象
@param keypath 被监听对象的属性名,不可为空,为空崩溃
@param options 有4种
(1)nskeyvalueobservingoptionnew 把更改之前的值提供给处理方法
(2)nskeyvalueobservingoptionold 把更改之后的值提供给处理方法
(3)nskeyvalueobservingoptioninitial 把初始化的值提供给处理方法,一旦注册,立马就会调用一次。通常它会带有新值,而不会带有旧值。
(4)nskeyvalueobservingoptionprior 分2次调用。在值改变之前和值改变之后
@param context 上下文
上述示例代码的运行结果如下所示:
2.手动监听
意思就是说:当某些需要控制监听过程的场景下,就需要手动监听,比如:为了尽量减少不必要的触发通知操作,或者当多个更改同时具备的时候才调用属性改变的监听方法。
实现手动监听的要点主要包括这几部分:
a.重写
(bool)automaticallynotifiesobserversforkey:(nsstring *)key
b.在set方法中在赋值的前后分别调用
willchangevalueforkey和didchangevalueforkey
2.1实现部分属性的手动监听
在animal.h中添加两个属性age和name,在animal.m中关闭age的自动监听功能,其它属性依然可以自动监听,在控制其中实现添加按钮点击按钮的时候改变age的值,并触发监听方法,代码如下:
animal类:
要实现类方法 automaticallynotifiesobserversforkey,并在其中设置对特定的 key 不自动发送通知(返回 no 即可)。这里要注意,对其它非手动实现的 key,要转交给 super 来处理[1,2,3]。
控制器:
当不点击按钮的时候,打印结果只打印了name属性的值:
当点击按钮之后,会手动触发监听,打印结果如下:
2.2所有属性都手动监听(禁止自动监听)
如果需要禁用该类kvo的话直接automaticallynotifiesobserversforkey返回no。
将animal.m中的类方法修改之后:
运行之后不点击按钮的话,age和name属性都不会自动调用监听方法:
点击了按钮之后,只有实现了手动监听的age属性调用了监听方法:
3.kvo的实现原理
当某一个类的实例第一次使用kvo的时候,系统就会在运行期间动态的创建该类的一个派生类,该类的命名规则一般是以nskvonotifying为前缀,以原本的类名为后缀。并且将原型的对象的isa指针指向该派生类。同时在派生类中重载了使用kvo的属性的setter方法,在重载的setter方法中实现真正的通知机制,正如前面我们手动实现kvo一样。这么做是基于设置属性会调用setter方法,而通过重写就获得了 kvo 需要的通知机制。当然前提是要通过遵循 kvo 的属性设置方式来变更属性值,如果仅是直接修改属性对应的成员变量,是无法实现 kvo 的[4,5]。
4.kvo与kvc、代理、通知的区别
1.与kvc的不同
kvc,即是指 nskeyvaluecoding,一个非正式的 protocol,提供一种机制来间接访问对象的属性,而不是通过调用setter、getter方法等 显式的存取方式去访问。kvo 就是基于 kvc 实现的关键技术之一。
kvo,即key-value observing,它提供一种机制,当指定的对象的属性被修改后,对象就会接受到通知。
2.与delegate的不同
和delegate一样,kvo和nsnotification的作用都是类与类之间的通信。但是与delegate不同的是:这两个都是负责发送接收通知,剩下的事情由系统处理,所以不用返回值;而delegate 则需要通信的对象通过变量(代理)联系;delegate只是一对一,而这两个可以一对多。delegate是非常严格的语法,需要定义很多代码。
3.和notification的区别
notification比kvo多了发送通知的一步。两者都是一对多,但是对象之间直接的交互,notification明显得多,需要notificationcenter来做为中间交互。而kvo如我们介绍的,设置观察者->处理属性变化,至于中间通知这一环,则隐秘多了,只留一句“交由系统通知”,具体的可参照以上实现过程的剖析。notification的优点是监听不局限于属性的变化,还可以对多种多样的状态变化进行监听,监听范围广,例如键盘、前后台等系统通知的使用也更显灵活方便[6,7]。
5.kvo实现过程中的注意事项
ios 10以下会有这些情况,ios11不会出现这些情况,但是为了代码的严谨性,以及以防出现无法预知的错误,还是避开这些比较好。
1、添加观察者次数与remove次数不匹配导致程序崩溃
连续对同一属性添加观察者是可以的,但是也要保证在移除观察者的时候也要移除对应次,不然可能会引发崩溃(ios11以上不会崩溃)。
当对同一个keypath进行两次removeobserver时会导致程序crash,这种情况常常出现在父类有一个kvo,父类在dealloc中remove了一次,子类又remove了一次的情况下。不要以为这种情况很少出现!当你封装framework开源给别人用或者多人协作开发时是有可能出现的,而且这种crash很难发现。不知道你发现没,目前的代码中context字段都是nil,那能否利用该字段来标识出到底kvo是superclass注册的,还是self注册的我们可以分别在父类以及本类中定义各自的context字符串,比如在本类中定义context为@thisismykvocontextnotsuper;然后在dealloc中remove observer时指定移除的自身添加的observer。这样ios就能知道移除的是自己的kvo,而不是父类中的kvo,避免二次remove造成crash[8]。
2、移除不存在的观察者(ios11以上不会崩溃)
当某个对象并没有添加观察者时,却执行了移除观察者的操作,也会导致程序崩溃,此处不附相关代码。
3、被观察者销毁时还存在观察者(ios11以上不会崩溃)
这种情况常出现在复杂逻辑下,观察者先于被观察者销毁[9]
4、kvo 行为是同步的,并且发生与所观察的值发生变化的同样的线程上。没有队列或者 run-loop 的处理。手动或者自动调用 -didchange… 会触发 kvo 通知。
所以,当我们试图从其他线程改变属性值的时候我们应当十分小心,除非能确定所有的观察者都用线程安全的方法处理 kvo 通知。通常来说,我们不推荐把 kvo 和多线程混起来。如果我们要用多个队列和线程,我们不应该在它们互相之间用 kvo[10]。
参考资料:
推荐阅读
企业全面移动化实践指南
基于人工智能场景的移动平台工程化实践
react native 移动技术在企业架构的应用
关于作者:小幸运,ios软件开发工程师,参与普元dev客户端oc代码的维护及新功能开发;使用普元移动开发平台开发邮政移动平台项目邮我行app。热爱互联网技术,努力拓宽技术面的程序媛一枚。
关于eaworld:微服务,devops,数据治理,移动架构原创技术分享。长按二维码关注!
在看点这里

南充分类信息网,免费分类信息发布

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录