侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计线程安全的懒加载属性包装器,支持值类型和引用类型并避免循环依赖

2025-12-14 / 0 评论 / 4 阅读

题目

设计线程安全的懒加载属性包装器,支持值类型和引用类型并避免循环依赖

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

属性包装器, 线程安全, 懒加载, 值类型与引用类型, 循环依赖

快速回答

实现线程安全的懒加载属性包装器需要解决以下核心问题:

  • 使用锁(如os_unfair_lock)确保多线程环境下的初始化安全
  • 通过内部引用容器支持值类型和引用类型
  • 使用@autoclosure延迟初始化执行
  • 在闭包中强制使用[weak self]避免循环依赖
  • 初始化后释放闭包减少内存占用
## 解析

原理说明

线程安全懒加载属性包装器需要解决三个核心问题:1) 多线程环境下初始化操作的原子性;2) 值类型(struct)和引用类型(class)的兼容性;3) 初始化闭包中的循环依赖风险。通过结合锁机制、内部引用容器和弱引用捕获,可实现安全高效的懒加载。

代码实现

import Foundation
import os.lock

@propertyWrapper
struct ThreadSafeLazy<T> {
    private final class Storage {
        var value: T?
    }

    private let storage = Storage()
    private var lock = os_unfair_lock()
    private var initializer: (() -> T)?

    init(wrappedValue initializer: @escaping @autoclosure () -> T) {
        self.initializer = initializer
    }

    var wrappedValue: T {
        mutating get {
            os_unfair_lock_lock(&lock)
            defer { os_unfair_lock_unlock(&lock) }

            if let value = storage.value {
                return value
            }

            guard let initializer = initializer else {
                fatalError("重复访问已释放的懒加载属性")
            }

            let value = initializer()
            storage.value = value
            self.initializer = nil  // 释放闭包
            return value
        }
        set {
            os_unfair_lock_lock(&lock)
            defer { os_unfair_lock_unlock(&lock) }
            storage.value = newValue
        }
    }
}

// 使用示例
class DataProcessor {
    @ThreadSafeLazy var heavyResource: HeavyResource = {
        // 强制使用weak避免循环依赖
        [weak self] in
        guard let self = self else { return HeavyResource() }
        return HeavyResource(config: self.config)
    }()

    var config: Configuration
    init(config: Configuration) {
        self.config = config
    }
}

struct Point {
    @ThreadSafeLazy var distanceCache: Double = {
        sqrt(x*x + y*y)  // 值类型安全使用
    }()
    var x, y: Double
}

最佳实践

  • 锁的选择:优先使用os_unfair_lock(性能优于NSLock
  • 内存管理:初始化后立即释放闭包,减少内存占用
  • 循环依赖防护:在闭包参数声明处强制使用[weak self]
  • 值类型支持:通过内部Storage类绕过值类型的复制语义
  • 错误处理:添加defer确保锁始终释放,避免死锁

常见错误

  • 线程竞争:未加锁导致多次初始化(使用os_unfair_lock_lock解决)
  • 循环引用:闭包内捕获强引用self(强制[weak self]
  • 值类型失效:直接存储值导致结构体复制时值丢失(通过引用容器解决)
  • 闭包滞留:初始化后未释放闭包(显式置nil

扩展知识

  • 锁的性能对比os_unfair_lock > pthread_mutex > NSLock > @synchronized
  • Swift 5.5+ 替代方案:使用actor隔离访问(但属性包装器不能直接声明为actor)
  • 内存布局影响:值类型中使用引用容器会增加一次指针跳转
  • 与标准lazy对比:标准lazy var既不线程安全也不支持弱引用捕获