• 深入理解objc_setAssociatedObject 和objc_getAssociatedObject这两个方法的机制和作用
  • 发布于 2个月前
  • 187 热度
    0 评论
前言
最近在 review 一些代码时,发现了objc_setAssociatedObject 和objc_getAssociatedObject 方法,突然忘了这两个方法的机制和作用。于是决定深入探究一番。

关联引用的定义
objc_setAssociatedObject 和objc_getAssociatedObject 被称为关联引用(Associative References)。根据苹果的官方文档:
objc_setAssociatedObject 用于通过给定的 key 和 value 关联策略为指定对象设置关联值。

objc_getAssociatedObject 用于返回与指定对象和 key 关联的 value。


简而言之,关联引用使我们能够通过键将一个对象链接或附加到另一个对象上。我们可以将第一个对象称为负载(payload),而第二个对象称为目标(target)。这种链接确保了当目标对象被释放时,负载也可以被释放。

创建对象与字符串的关联
我们可以利用objc_setAssociatedObject 方法将负载(例如 "Example message")附加到 UIViewController 上,注意,这里的 self 就是控制器。
enum AssociatedKey {
    staticvar key: Int = 0
}

func onButtonTapped() {
    // 创建 Alert和负载
    let alert = UIAlertController(title: "Title", message: "Example message", preferredStyle: .alert)
    let payload = "Example message"
    // 使用 objc_setAssociatedObject 函数将负载与 Alert 关联
    objc_setAssociatedObject(self, &AssociatedKey.key, payload, .OBJC_ASSOCIATION_RETAIN)
    // 为 Alert 添加一个 OK 按钮
    let okAction = UIAlertAction(title: "OK", style: .default) { [weakself] _in
        self?.handleAlertDismissed()
    }
    alert.addAction(okAction)
    // 堆代码 duidaima.com
    // 显示 Alert 
    present(alert, animated: true, completion: nil)
}
代码解析
在上面的代码中,我们首先创建一个唯一的键,用于每个关联。使用静态变量作为 key 是比较推荐的做法。接下来,我们创建一个 Alert,并将负载存储在一个变量中。这个 Alert 稍后会被调用以检索关联对象。objc_setAssociatedObject 函数将负载与视图控制器关联。这个函数需要四个参数:源对象或目标对象、键、值或负载,以及关联策略常量。

第四个参数指定关联策略。此策略决定了在关联中,源对象(目标对象)与值(负载)之间的引用类型。它可以是“弱引用”(OBJC_ASSOCIATION_ASSIGN)、“强引用”(OBJC_ASSOCIATION_RETAIN)或复制(OBJC_ASSOCIATION_COPY)。此外,它还指定关联是以原子方式还是非原子方式进行的。

检索关联对象
我们使用objc_getAssociatedObject 函数来检索关联对象。
func handleAlertDismissed() {
    // 使用 objc_getAssociatedObject 检索关联负载
    if let payload = objc_getAssociatedObject(self, &AssociatedKey.key) {
        // 打印负载
        print("Payload is: \(payload)") // 输出 "Payload is: Example message"
    }
}
释放关键对象
如果你想解除关联并释放负载而不分配新的对象,你可以调用objc_setAssociatedObject,将值设为 nil。
objc_setAssociatedObject(self, &AssociatedKey.key, nil, .OBJC_ASSOCIATION_RETAIN)
完整代码示例
class ViewController: UIViewController {

    // MARK: - Enums
    
    // 用于关联的静态变量键
    enum AssociatedKey {
        staticvar key: Int = 0
    }
    
    // MARK: - Properties
    
    privatevar button: UIButton = {
        let button = UIButton()
        button.setTitle("Tap me", for: .normal)
        button.backgroundColor = .blue
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    // MARK: - View Lifecycles
    
    overridefunc viewDidLoad() {
        super.viewDidLoad()
        setupUI()
        button.addTarget(self, action: #selector(onButtonTapped), for: .touchUpInside)
    }
}

// MARK: - Private Methods
extension ViewController {
    
    func setupUI() {
        view.addSubview(button)
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    }
    
    @objcfunc onButtonTapped() {
        // 创建 Alert 和负载
        let alert = UIAlertController(title: "Title", message: "Example message", preferredStyle: .alert)
        let payload = "Example message"
        
        // 使用 objc_setAssociatedObject 函数将负载与 Alert 关联
        objc_setAssociatedObject(self, &AssociatedKey.key, payload, .OBJC_ASSOCIATION_RETAIN)
        
        // 为 Alert 添加一个 OK 按钮
        let okAction = UIAlertAction(title: "OK", style: .default) { [weakself] _in
            self?.handleAlertDismissed()
        }
        alert.addAction(okAction)
        // 堆代码 duidaima.com
        // 显示 Alert 
        present(alert, animated: true, completion: nil)
    }
    
    func handleAlertDismissed() {
        // 使用 objc_getAssociatedObject 检索关联负载
        iflet payload = objc_getAssociatedObject(self, &AssociatedKey.key) {
            // 打印负载
            print("Payload is: \(payload)") // 输出 "Payload is: Example message"
        }
    }
}
总结
通过关联引用,我们可以灵活地将对象之间进行关联,而不必直接修改类的结构,增加了代码的灵活性和扩展性。通过本文的介绍,希望大家对objc_setAssociatedObject 和objc_getAssociatedObject 有一个了解,能够在实际开发中得心应手地使用这两个方法。
用户评论