繁体   English   中英

当我在 swift 中收到新通知时,我应该使用弱自我吗?

[英]Should I use weak self when I getting new Notification in swift?

我正在发出通知并且我也收到通知并且应用程序可以运行,我的应用程序可以在有或没有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 ,因为NotificationCenter必须强烈地坚持闭包。 如果你不使用weak self ,你的ViewController永远不会被破坏。

但是你还应该确保当ViewController被销毁时你的观察者也被销毁。 addObserver(forName:object:queue:using:)方法返回一个 object ,您应该将其保存在实例属性中并传递给deinit中的NotificationCenter.default.removeObserver

class ViewController: NSViewController {
    private var observer: NSObjectProtocol? = nil

    deinit {
        if let observer {
            NotificationCenter.default.removeObserver(observer)
        }
    }

    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)
            }
        }

        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
    }
}

但我建议使用不同的 API,这样您就不必编写deinit 改为使用 Combine,如下所示:

import Combine
import SwiftUI

class ViewController: NSViewController {
    private var tickets: [AnyCancellable] = []

    override func viewWillAppear() {
        NotificationCenter.default.publisher(for: myNotificationName)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] in
                if let self, let newTitle = $0.object as? String {
                    self.titleUpdater(value: newTitle)
                }
            }
            .store(in: &tickets)

        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
    }
}

这样,您就不必编写deinit ViewController被销毁时,它会自动销毁其实例属性,包括tickets数组及其内容。 sink返回的票证( AnyCancellable )被销毁时,它取消订阅。

您甚至可以完全消除对self的使用。 您没有显示appName的声明。 如果它是全球性的,您可以这样订阅:

        NotificationCenter.default.publisher(for: myNotificationName)
            .receive(on: DispatchQueue.main)
            .sink { [view] in
                if let newTitle = $0.object as? String {
                    appName = newTitle
                    view.window?.title = newTitle
                }
            }
            .store(in: &tickets)

现在闭包直接捕获view而不是捕获self ,所以不存在保留循环的可能性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM