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

相关推荐
阿祖zu11 小时前
芝士就是力量!开源私有化部署与 GitHub 双向同步的个人知识笔记 App
前端·后端·ios
YH552698417 小时前
MacBook Air M4 和 M5 怎么选?
macos
GitLqr18 小时前
iOS 27 强制要求 UISceneDelegate:UIKit 和 Flutter 开发者该如何应对?
flutter·ios·全栈
MoSTChillax18 小时前
数字人竞品分析(中):核心功能、产品使用与技术能力对比
ide·macos·xcode
屑曦晨20 小时前
PKToolPicker工具栏图标错误模糊问题排查报告
ios
2501_9159214321 小时前
详细解析,iOS 应用上架 App Store 的完整流程与指南
android·ios·小程序·https·uni-app·iphone·webview
2501_9151063221 小时前
SwiftUI项目创建详解:使用Xcode从零开始创建第一个App项目
ide·vscode·ios·swiftui·个人开发·xcode·敏捷流程
2501_9159184121 小时前
Xcode调试iOS应用内存管理与优化,识别泄漏与性能监控指南
android·ios·小程序·uni-app·cocoa·iphone·xcode
9765033351 天前
iOS 上架/审核 4.3a Cocos 2026最新方案解读
flutter·ios·swift·cocos2d·ios开发
2501_916007471 天前
IDE 是什么?集成开发环境详解与 iOS 开发选型指南
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程