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没有被释放,才能使用无主引用。

相关推荐
食尘者2 小时前
MacBook Pro M5Max 本地大模型推理实践
macos·aigc
x-cmd13 小时前
Mac 涨价后,本地 AI 还能千元入门吗?
linux·人工智能·macos·ai·agent·amd·本地ai入门
迷雾悠悠17 小时前
企业微信iPad协议聚合聊天系统:从原理到实战开发指南
ios·企业微信·ipad
LDyun_21 小时前
.2026主流云手机测评,8款机型高压挂机实测
ios·智能手机·安卓·玩游戏
m0_377108141 天前
小智ai-esp32
人工智能·macos·xcode
pop_xiaoli1 天前
【iOS】YYModel源码学习
学习·ios·objective-c·cocoa
霍霍哈嗨1 天前
swiftUI框架基础
ios·swiftui·swift
末代iOS程序员华仔1 天前
iOS 开发到上架 App Store 全流程详解
ios·objective-c·swift
2501_916007471 天前
抓包工具对比 Charles、Fiddler、TraceEagle、Wireshark 与 Proxyman
前端·网络协议·ios·adb·https·fiddler·wireshark