Swift - Associated Object
不知道是从什么时候开始,**“是否能通过Category 给已有的类添加成员变量”**就成为了一道Objective-C 面试中的常见题目。不幸的消息是这个面试题目在Swift 中可能依旧会存在。
得益于Objective-C 的运行时
(runtime)和键值编程
(Key-Value Coding)的特性,我们可以在运行时向一个对象添加至存储
。而在使用Category 扩展
现有的类的功能的时候,直接添加实例变量这种行为是不被允许的,这时候一般就使用property 配合Associated Object
的方式,将一个对象“关联”
到已有的要扩展的对象上。进行关联后,在对这个目标对象访问的时候,从外界看来,就似乎是直接通过属性访问对象的实例变量一样,可以说非常方便。
在Swift 中这样的方法依旧有效,只不过在写法上
可能有些不同
。两个对应的运行时的ge
t 和set Associated Object
的API
是这样的:
OBJC_EXPORT void
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key,
id _Nullable value, objc_AssociationPolicy policy)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
OBJC_EXPORT id _Nullable
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0);
这两个API 在Swift 中使用时,参数会自动变化,并且因为Swift 的安全性,在类型检查上严格了不少,因此我们有必要也进行一些调整。在Swift 中向某个extension
使用Associated Object
的方式将对象进行关联的写法是:
class MyClass {
}
private var key: Void?
extension MyClass {
var title: String? {
get {
return objc_getAssociatedObject(self, &key) as? String
}
set {
objc_setAssociatedObject(self, &key, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
func printTitle(input: MyClass) {
if let title = input.title {
print("Title \(title)")
} else {
print("没有设置")
}
}
// 测试
let a = MyClass()
printTitle(input: a)
a.title = "biao ti"
printTitle(input: a)
// 输出
// 没有设置
// Title biao ti
key
的类型在这里声明为了Void?
,通过& 操作符
取地址并作为UnsafeRawPointer 类型
被传入。这在Swift 与C 协作和指针操作时是一种很常见的用法。关于C 的指针操作和这些Unsafe 开头的类型的用法,可以参看UnsafePointer的内容
上一篇: Swift学习一 初步认识
下一篇: AVAudioRecorder进行录音