Swift中接收Notification时是否需要使用weak self?
我在项目中发送并接收通知,无论是否使用weak self,应用均能正常运行,因此我对是否需要使用weak self感到困惑,希望有人能解惑,说明此处是否需要使用weak self以及原因。
本代码适用于macOS Storyboard Cocoa项目:
import Cocoa import SwiftUI var appName: String = "My App Name" class ViewController: NSViewController { override func viewWillAppear() { NotificationCenter.default.addObserver(forName: myNotificationName, object: nil, queue: .main) { [weak self] newNotification in if let unwrappedNewNotification = newNotification.object as? String { self?.titleUpdater(value: unwrappedNewNotification) } } let controller = NSHostingController(rootView: ContentView()) self.view = controller.view self.view.window?.title = appName } private func titleUpdater(value: String) { appName = value self.view.window?.title = value } } struct ContentView: View { var body: some View { VStack { Button("Change", action: { postMyNotification(value: appName + " updated!") }) } .frame(width: 400.0, height: 300.0) } } let myNotificationName: Notification.Name = Notification.Name(rawValue: "myNotificationName") func postMyNotification(value: String) { NotificationCenter.default.post(Notification(name: myNotificationName, object: value)) }
更新内容:
deinit { if let unwrappedObserver: NSObjectProtocol = observer { print("worked for deinit!") NotificationCenter.default.removeObserver(unwrappedObserver) } }
结论:必须使用[weak self],原因如下:
防止循环引用引发内存泄漏
当闭包中直接用self(不加weak)时,闭包会对ViewController实例产生强引用,而NotificationCenter会持有这个闭包直到观察者被移除。如果ViewController本该被销毁(比如窗口关闭),但闭包的强引用会把它“挂”在内存里,导致deinit永远不会执行——你写的那段deinit代码根本不会打印日志,内存泄漏就这么发生了。运行正常不代表没有隐患
现在应用没崩溃,是因为内存泄漏不会立刻触发问题,但长期运行会让内存占用持续升高,轻则拖慢应用,重则触发系统内存警告甚至崩溃。只有在ViewController销毁的场景下,才能暴露这个问题。保证代码安全
用[weak self]后,self变成可选值,当ViewController已经被销毁时,self?.titleUpdater(...)会安全跳过,不会访问已经释放的内存。如果不用weak,万一Notification在ViewController销毁后被触发,就可能出现野指针访问,导致不可预料的崩溃。
补充:完善你的观察者管理代码
你现在的代码里没看到observer变量的声明,需要在ViewController中添加一个属性来保存观察者实例,这样才能在deinit中正确移除:
class ViewController: NSViewController { private var observer: NSObjectProtocol? // 新增属性 override func viewWillAppear() { // 保存返回的观察者实例 observer = NotificationCenter.default.addObserver(forName: myNotificationName, object: nil, queue: .main) { [weak self] newNotification in if let unwrappedNewNotification = newNotification.object as? String { self?.titleUpdater(value: unwrappedNewNotification) } } // ... 其他代码 } deinit { if let unwrappedObserver = observer { print("worked for deinit!") NotificationCenter.default.removeObserver(unwrappedObserver) } } }
这样配合[weak self],才能确保ViewController可以正常销毁,彻底避免内存泄漏问题。
内容的提问来源于stack exchange,提问作者swiftPunk

