Swift 循环引用

为什么Swift 会发生循环引用

A的实例持有B的实例,B的实例持有A的实例。

如何解决循环引用

使用弱引用,无主引用 本质是断开循环引用

弱引用

代理设计模式

swift 复制代码
/// NonisolatedNonsending 
protocol DownloadManagerDelegate: AnyObject {
    func downloadManager(_ manager: DownloadManager, didProgressTo percentage: Double)
}

class DownloadManager {
    weak var delegate: DownloadManagerDelegate?
    func startDownload() async {
        for i in 0...100 {
            try? await Task.sleep(nanoseconds: 500_000_000)
            self.delegate?.downloadManager(self, didProgressTo: Double(i) / 100)
        }
    }
}

class ViewController: NSViewController, DownloadManagerDelegate {
    var manager = DownloadManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self
        Task {
            await manager.startDownload()
        }
    }
    func downloadManager(_ manager: DownloadManager, didProgressTo percentage: Double) {
        print("download: \(percentage)")
    }
}

ViewController 的生命周期 > DownloadManager 的生命周期,这里使用weak var optional. ViewController 的实例持有DownloadManager 的实例,DownloadManager 的实例并没有持有ViewController,打破了循环引用。当ViewController销毁时,DownloadManager 的实例也会销毁。

闭包

swift 复制代码
class DownloadManager {
    var onProgress: ((Double) -> Void)?
    func startDownload() async {
        for i in 0...100 {
            try? await Task.sleep(nanoseconds: 500_000_000)
            let percentage = Double(i) / 100
            if let onProgress {
                onProgress(percentage)
            }
        }
    }
}

class ViewController: NSViewController {
    var manager = DownloadManager()
    override func viewDidLoad() {
        super.viewDidLoad()
        manager.onProgress = { [weak self] percentage in 
            self?.updateUI(progress: percentage)
        }
        Task {
            await manager.startDownload()
        }
    }
    func updateUI(progress: Double) {}
}

解释:这里的ViewController 的实例持有manager, manager 的onProgress 使用捕获列表 使用weak关键字,弱引用了ViewController 的实例,从而打破了循环引用。

无主引用

swift 复制代码
class Greeter {
    var name: String?
    lazy var greeting = { [unowned self] in
        return "Hello, \(self.name ?? "Swift")"
    }
    deinit {
        print("Greeter deinit")
    }
}
// Client
let greeter = Greeter()
let greeting = greeter.greeting()
print(greeting)
// **Hello, Swift**
// **Greeter deinit**

和弱引用类似,无主引用也不会引用计数+1。Greeter 实例持有了greeting, greeting 无主引用了Greeter 实例, 当greeter 除了作用域后,Greeter 实例释放。 和弱引用不同的是,无主引用无需解包,使用更简洁。然而,这也会给我们带来风险。使用无主引用时,一定要确保self一定存在。在上例中,lazy var greeting的闭包是在第一次访问 greeting属性时才被创建并存储的。此时 self(Greeter实例)必然存在,因为你必须通过某个实例才能访问。

swift 复制代码
class Greeter {
    var name: String?
    lazy var greeting = {
        return "Hello, \(self.name ?? "Swift")"
    }()
    deinit {
        print("Greeter deinit")
    }
}
let greeter = Greeter()
let greeting: String = greeter.greeting
print(greeting)
// **Hello, Swift**
// **Greeter deinit**

此时greet不再是闭包类型(String)。Greeter并没有持有返回字符串的闭包。闭包执行完就释放了,所以此时不存在循环引用。

再看一个变体。

swift 复制代码
class Greeter {
    var name: String?
    lazy var greeting = { [name] in
        return "Hello, \(name ?? "Swift")"
    }
    deinit {
        print("Greeter deinit")
    }
}
let greeter = Greeter()
let greeting = greeter.greeting()
print(greeting)
// **Hello, Swift**
// **Greeter deinit**

此时我们捕获了name,因为 name是值类型,闭包捕获的是它的一个副本, greeting 闭包不再持有 self,所以不存在循环引用。

总结

解决循环引用时,大部分的情况都可以使用弱引用;只有在闭包执行时,确定self没有被释放,才能使用无主引用。

相关推荐
fengye20716119 小时前
实验:CPython 迭代协议底层验证(`PyObject_GetIter` / `PyIter_Next`)
macos
2501_916007471 天前
抓包工具功能对比,TraceEagle、Charles、Fiddler、Wireshark、mitmproxy 差别
前端·网络协议·ios·adb·https·fiddler·wireshark
DevLoom_1 天前
macOS 命令行 curl/git 报 SSL 证书错误?eMudhra 事件完整修复指南
git·macos·ssl
2501_915106321 天前
Windows 上开发 Unity iOS,使用 KXBuild 进行真机调试、性能采样与打包 ipa
windows·vscode·unity·ios·个人开发·swift·敏捷流程
柚鸥ASO优化1 天前
iOS 27.2 更新后,App Store 广告展示逻辑出现新变化
ios
美狐美颜SDK开放平台1 天前
视频美颜SDK如何应对快速转头与大幅度面部动作?
android·人工智能·ios·音视频·直播美颜sdk·视频美颜sdk
茶底世界之下1 天前
秋叶已经够黄了:用 Awaking 单独收一点黄色
ios
Jewel1051 天前
文字为什么总是不居中!记录一个bug
android·flutter·ios
清水白石0081 天前
Python 安装与环境配置:Windows、macOS、Linux 完整指南(第2集)
windows·python·macos
hhhjhl1 天前
开源鸿蒙平台 CMP 三方库「compose-macos-26-ui」适配全流程
macos·开源·harmonyos