老调重弹:对kvo的封装思路

简介: 关于kvo,kvo能做什么?kvo作为cocoa框架的重要特性之一,在底层框架中被大量使用。在特定的场合使用该特性往往能够带来难以想象的好处,让整个方案变得相当简洁和优雅。

关于kvo,kvo能做什么?

kvo作为cocoa框架的重要特性之一,在底层框架中被大量使用。在特定的场合使用该特性往往能够带来难以想象的好处,让整个方案变得相当简洁和优雅。比如大名鼎鼎的下拉刷新的svpulltorefresh框架,其实现采用了category动态添加属性和kvo结合的方案,在egoRefresh框架的基础上获得了极大的改善,使调用者所要书写的代码量直接下降了一个量级。其中的奥秘在于通过kvo很好的处理了frame变化的问题,调用者不用再处理frame相关的代码,仅需要聚焦下拉刷新带来的业务逻辑事件本身。此后所有流行的下拉刷新框架无一例外的都采用了此方案。仅仅想象一下kvo本身的灵活性、广泛的适用性而言,其强大毋庸置疑;但是如何把它用好,则是一个很值得研究的课题。

直接使用kvo的麻烦?

麻烦1:

对于kvo来说,我认为他最大的坑在于生命周期的管理。举个例子一个对象A一旦发起了对别的对象属性的观察以后,在A对象释放之前,必须要手动调用解除监听的代码;否则在该对象属性变化时,则会引起crash。其实delegate以前也有类似的问题,但在iOS的演进过程中这个问题被解决了。解决方案是这样:在ARC以后delegate应该被设置成weak属性,这样一来当代理者被释放时,雇主的delegate属性是weak的情形中,这个delegate属性会被自动置为空,向一个空对象发消息,不会有任何问题。但是kvo的特性比较古老,苹果也未对其实现做任何改进,因此生命周期的管理对于合理的使用kvo非常重要。

麻烦2:

在另外一种场景下的使用也会crash:当一个对象A在没有发起观察之前就调用解除监听的代码时。所以很多人在调用解除监听的代码时都会引入try-catch来捕获这个错误,从而增加程序的健壮性。这也是一个不得已而为之的无奈之举。

麻烦3:

其他: kvo的调用代码本身略显繁琐,所有的kvo处理代码都被指定在一个方法内部,这样在监听多个对象时会导致该方法内部相当混乱,且该处理方法和调用方法一样,拥有4个参数,其中2个参数几乎可以说是没有作用(一般会被固定填写相同的值); 其中对于属性字段传递的参数是通过字符串来表示的,如果拼写错误也容易带来问题且难以检查(编译器不会告警)。这也是经常为人吐槽或者诟病的地方;人们总希望api清晰简洁,如果能够支持像block这样的现代语法则更棒。

kvo的封装思路

如上,我们认为kvo对于我们的技术链来说是一个不可或缺的环节;但是直接使用它的话,又有一些麻烦需要解决。所以,我们需要对它进行封装,封装的目的就是为了解决上面所列举的麻烦。
解决麻烦1:封装一个对象(我们将它看作是一个专门处理kvo的代理),专门用来管理属性的监听事件;此对象和一次监听动作唯一绑定;在该代理的dealloc里面我们会自动的调用解注册的方法。
解决麻烦2:在此前封装的代理对象中,我们会记录在此对象中的监听事件,如此一来我们自动调用的解注册方法和对外提供的解注册方法都能够保证安全(监听和解监听是一一对应的关系,且在时序上是监听在前,解监听在后)。
解决麻烦3: 封装api方法,支持block或target action的操作,并去除冗余参数。实际需要监听kvo的对象持有上面封装的代理对象,并从代理对象转发回调block或sel方法到实际需要监听kvo的对象。
这样一来我们就完成了一个简单的封装。我们还可以通过category和一些runtime技术的结合,来使得我们的api更简洁,使用更方便;这里就不再展开了。

More:封装示例+Demo

https://github.com/X-Team-X/SimpleKVO
上面的库是笔者从NSObject+YYAddForKVO简单修改而来,感谢ibireme对开源所作的贡献!
YYKit 原地址(author ibireme): https://github.com/ibireme/YYKit

参考和致谢

http://www.cocoachina.com/industry/20131106/7303.html
http://nshipster.com/key-value-observing/
https://github.com/th-in-gs/THObserversAndBinders
https://github.com/ibireme/YYKit/blob/master/YYKit/Base/Foundation/NSObject%2BYYAddForKVO.h
本文的写作参考了上述的文章和开源代码,也在此对其作者们表达感谢。

目录
相关文章
|
Web App开发 iOS开发 Windows
ios获取原生系统应用的包名
ios获取原生系统应用的包名
2970 0
|
1月前
|
人工智能 自然语言处理 安全
SOFA AI 网关基于 Higress 的落地实践
SOFA 商业化团队为满足客户 AI 业务的发展需求,基于开源 Higress 内核构建,推出了 SOFA AI 网关,专为 SOFA 场景深度优化、能力增强,是面向 AI 需求的智能网关解决方案。
205 15
|
1月前
|
供应链 搜索推荐 API
从0到1掌握1688API:图片搜索获取技巧与避坑指南
1688图片搜索API基于图像识别技术,支持上传JPG/PNG格式图片(Base64或URL),实现同款或相似商品搜索。适用于电商选品、供应链管理等场景,提供价格、销量等多维度筛选,返回商品ID、标题、价格、销量及供应商信息。
|
5月前
|
人工智能 自然语言处理 API
电商API技术文档编写规范白皮书:方法论与行业实践
本文系统阐述电商API接口文档的编写规范与最佳实践,涵盖结构设计、技术语言、开发者体验、版本控制及质量保障等方面,助力企业提升开发效率,构建开放共赢的电商生态。
|
机器学习/深度学习 算法 算法框架/工具
埃式质数筛及性质
【10月更文挑战第8天】本文介绍质数,或素数,指大于1且仅能被1和自身整除的自然数。它们在数学中有独特地位,如算术基本定理指出任何大于1的自然数可唯一分解为质数乘积。质数的寻找方法多样,包括试除法、埃拉托斯特尼筛法等,后者通过筛除合数高效找出质数。质数在密码学中尤为重要,如RSA加密算法依赖大质数的乘积安全性。此外,还有多种算法和理论,如欧拉筛法、费马小定理、梅森质数等,丰富了质数的研究领域。
464 1
|
消息中间件 数据采集 Cloud Native
iLogtail 开源贡献人物专访:技术之路无坦途,与社区共同成长
在 iLogtail 开源两周年这一里程碑时刻,我们邀请到了两位社区 Committer 进行分享,揭秘这些开发者如何在日常工作中与 iLogtail 结缘,又如何在业余时间里为项目添砖加瓦,推动其不断向前发展~
346 97
|
8月前
|
机器学习/深度学习 人工智能 自然语言处理
什么是智能搜索
智能搜索融合了人工智能和大数据技术,提供高效的语义理解、多模态数据处理及个性化推荐。它不仅支持传统关键词匹配,还结合NLP、机器学习等先进技术,提升信息检索的精准度与多样性。适用于电商、内容平台、多媒体及企业内部知识库等多种场景,显著优化用户体验和业务效率。
1193 2
|
9月前
|
Java Unix 程序员
一文彻底搞定C语言的前世今生
C语言是计算机编程史上的一颗璀璨恒星,由贝尔实验室的肯·汤普逊和丹尼斯·里奇在20世纪70年代基于B语言开发。它凭借高效、灵活、可移植性强等特点迅速崛起,成为Unix操作系统的核心语言,并广泛应用于操作系统、嵌入式系统、游戏引擎等领域。C语言不仅推动了众多后续编程语言的发展,如C++、Java等,还通过多次标准化(C89、C99、C11等)不断适应新时代的需求,至今仍占据着计算机技术的重要地位。
278 0
|
Java
为什么Java不支持多继承
本文讨论了Java不支持多继承的原因,包括避免菱形继承问题、简化编程语言和防止层次膨胀,同时提供了实现多继承效果的替代方案,如实现多接口、使用组合和继承加接口的方式。
380 0
为什么Java不支持多继承
|
Android开发 Swift iOS开发
python 基于电脑蓝牙连接获取手机的实时数据
python 基于电脑蓝牙连接获取手机的实时数据
333 0