初级Swift开发者常见错误:高级篇
当你从编写简单的SwiftUI界面转向构建具有自定义架构的复杂多线程应用时,潜在陷阱的数量会急剧增加。许多开发者在掌握了Swift的基本语法后,便认为自己已准备好投入生产环境,但却会犯下导致内存泄漏、运行不稳定以及难以追踪的bug等严重错误。在本文中,我们将分析中级开发者最常遇到的四类错误。
1. 闭包中的内存泄漏:不仅仅是 [weak self]
最常见的错误是在不理解何时真正需要的情况下使用 [weak self]。许多开发者在所有闭包中都加上 weak self,这会导致对象过早被销毁。或者,他们会在不明显的地方忘记处理强引用循环。
典型错误: 在闭包内部创建对持有该闭包的对象的强引用。
class NetworkManager { var onCompletion: (() -> Void)? func fetchData() { // 错误:闭包强捕获了 self onCompletion = { self.updateUI() } } func updateUI() { print("UI 已更新") }}正确解决方案: 根据对象的生命周期使用 [weak self] 或 [unowned self]。
class NetworkManager { var onCompletion: (() -> Void)? func fetchData() { onCompletion = { [weak self] in guard let self = self else { return } self.updateUI() } } func updateUI() { print("UI 已更新") }}高级细节: 谨慎使用 [unowned self]。如果对象被释放而闭包被调用,应用将会崩溃。只有在100%确定对象生命周期长于闭包时,才使用 unowned。
2. async/await 和 Actor 的不当使用
随着 Swift 5.5 中 Structured Concurrency 的出现,许多开发者开始积极使用 async/await,但忘记了 MainActor 和 Sendable。这会导致竞态条件以及在后台线程更新UI的问题。
典型错误: 调用一个本应在主线程执行的异步函数,但没有显式标记 @MainActor。
class ViewModel: ObservableObject { @Published var data: [String] = [] // 错误:函数未标记 @MainActor func loadData() async { let result = await fetchFromNetwork() // 这可能在后台线程执行! self.data = result } func fetchFromNetwork() async -> [String] { try? await Task.sleep(nanoseconds: 1_000_000_000) return ["Item1", "Item2"] }}正确解决方案: 为更新UI的函数使用 @MainActor。
class ViewModel: ObservableObject { @Published var data: [String] = [] @MainActor // 现在保证在主线程执行 func loadData() async { let result = await fetchFromNetwork() self.data = result } func fetchFromNetwork() async -> [String] { try? await Task.sleep(nanoseconds: 1_000_000_000) return ["Item1", "Item2"] }}高级细节: 不要忘记 nonisolated 和 actor。如果你使用 actor,所有公开方法默认是异步的。如果你没有仔细考虑隔离性,这可能会导致意外的延迟。