协议、关联对象、KVO等Swift和OC的关系。
一、协议
1.1. 只能被class继承的协议
示例代码:
1 | protocol Runnable1: AnyObject { } |
被@objc
修饰的协议,还可以暴露给OC去遵守实现。
1.2. 可选协议
正常情况下,Swift定义的协议内容都需要实现,如果需要可选实现,可以定义一个协议扩展,在扩展中空实现需要可选实现的协议。
也可以通过@objc
定义可选协议,这种协议只能被class
遵守。
示例代码:
1 | @objc protocol Runnable { |
加上optional
就必须加@objc
。并且只能被类实现协议。
二、dynamic
被@objc、dynamic
修饰的内容会具有动态性,比如调用方法会走runtime
那一套流程。
示例代码:
1 | class Dog: NSObject { |
test1
汇编(消息转发):
test2
汇编(虚表):
三、KVC/KVO
Swift支持KVC/KVO的条件:
- 属性所在的类、监听器最终继承自
NSObject
(因为OC的KVC/KVO走的是runtime
,而使用runtime
必然会用isa
,isa
又是NSObject
的) - 用
@objc dynamic
修饰对应的属性
示例代码:
1 | class Observer: NSObject { |
如果觉得上面的代码还需要初始化观察者对象比较麻烦,还可以使用block
方式的KVO。
示例代码:
1 | class Person: NSObject { |
注意监听的属性前面要加上斜杠\
。
四、关联对象
在Swift中,class
依然可以使用关联对象。
默认情况下,extension
不可以增加存储属性。借助关联对象,可以实现类似extension
为class
增加存储属性的效果。
示例代码:
1 | class Person { } |
关联对象的本质就是键值对,但是需要我们自己绑定一个编译期已知地址值(静态存储属性)。由于仅需要地址绑定,所以为了内存空间考虑,静态存储属性使用Bool
类型或者Void?
类型最好(仅占用1个字节)。
五、资源名管理
在项目开发中,经常有可能会遇到一张图片好多地方在使用(图片名为标识),一个标题很多地方使用等等。其实我们可以把这些相同的资源统一起来做一个标识符,防止后期多处修改。
示例代码一:
1 | let img = UIImage(named: "logo") |
如上面示例代码,如果很多地方都用到了名字为logo
的图片,描述为按钮
的文字等,一旦遇到修改,简直是地狱一般(虽然可以全局修改,但是有可能会一键修改到工程死翘翘)。
遇到这种资源名管理,我们可以有多种处理方式。下面介绍下参考Android的资源名管理方式:
示例代码二:
1 | enum R { |
R
代表Resource
,Swift仿Android的写法,Swift主要利用了枚举的关联值。
示例代码三:
1 | // 原始 |
上面对image
的封装有两点考量:
可以直接通过名称返回一个
Image
对象。静态属性在内存中只有一份,后面任何地方再次用到时可直接从内存中加载数据(除非需要每次都加载新数据)。
更多优秀的思路参考: