iOS 关联对象

850 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情

前言

首先我们先来看一个经典的面试题

可以给Category(或者 Swift中类的拓展)添加成员变量么?

这是一个经典的面试题. 通过源码, 我们可以很直观的看到, 分类的结构体 category_t 并没有 ivas 成员变量, 所以我们不可以直接给Category 添加成员变量

但是 category_t 中有 属性列表 properties, 那么也就是说可以给属性添加属性, 但是这跟本来的属性有什么区别呢?

本类中的属性, 编译器会自动帮我们生成setter和getter方法, 但是Category中的属性并不会, 所以当在Category我们声明了一个属性, 在给属性赋值时, 就会报错。

所以, 这就需要我们自己手动给分类的属性添加setter和getter方法. 但是分类又不能添加成员变量, 那么我们的setter方法给谁赋值? getter 又返回谁? 下面我们以Swift 类拓展添加关联对象做举例。

关联对象技术

我们可以通过关联对象间接给一个Swift的类拓展添加一个类似成员变量的东西. 关联对象相关有三个主要的函数

/** 给一个对象设置关联对象
object: 需要添加关联的源对象
key: 关联值的唯一key
value: 关联的具体值
policy: 关联的策略 (类似声明properties的参数)
*/
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

/** 获取某个对象的某个关联对象
object: 需要获取关联的源对象
key: 关联值的唯一key
*/
id objc_getAssociatedObject(id object, const void *key)

/** 移除对象的关联对象
 object: 需要移除的源对象
*/
void objc_removeAssociatedObjects(id object) 

通过关联对象技术, 我们可以在setter方法中, 给 cName 赋值, 在getter方法中获取 cName 的值

demo

代码举例(给UIButton的拓展添加一个count属性)

extension UIButton {
    private static var ADJ_KEY: Void?
    var count: Int {
        get {
            (objc_getAssociatedObject(self, &Self.ADJ_KEY) as? Int) ?? 0
        }
        set {
            objc_setAssociatedObject(self, &Self.ADJ_KEY, newValue,     .OBJC_ASSOCIATION_COPY)
        }
    }
}

使用

override func viewDidLoad() {
    super.viewDidLoad()
    let test = UIButton()
    test.count = 200
    print(test.count)
}

参考

【iOS】关联对象详解 - 简书 (jianshu.com)