1

I am posing a notification and also i am getting the notification and the app works, my app would be working with or without weak self so I am little confused about using or not using it, maybe some one could help me and show me if i need weak self and why I need it or why i do not need it here.

This code is for macOS Storyboard Cocoa project.

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

Update:

deinit {
    if let unwrappedObserver: NSObjectProtocol = observer {
        print("worked for deinit!")
        NotificationCenter.default.removeObserver(unwrappedObserver)
    }
}
ios coder
  • 1
  • 4
  • 31
  • 91
  • 1
    https://www.avanderlee.com/swift/weak-self/ – jnpdx Jan 31 '23 at 18:28
  • 2
    Lots of good insights here: [What is the difference between a weak reference and an unowned reference?](https://stackoverflow.com/questions/24011575/what-is-the-difference-between-a-weak-reference-and-an-unowned-reference) – HangarRash Jan 31 '23 at 18:45
  • @jnpdx: I read that article could not help myself, it defines imaginary examples and issues which we never going have those issue in real project. I do not recommend it to others. – ios coder Jan 31 '23 at 19:05
  • Strongly disagree -- programming issues almost _always_ are defined in abstract ways. I highly recommend that article, but I understand it may not be useful for everyone. – jnpdx Jan 31 '23 at 19:30
  • @jnpdx: I think it was difficult for me to understand or find a use case from examples. – ios coder Jan 31 '23 at 21:03

1 Answers1

4

Yes, you should use weak self in this case, because NotificationCenter has to hold on to the closure strongly. If you don't use weak self, your ViewController can never be destroyed.

But you should also ensure that your observer is destroyed when the ViewController is destroyed. The addObserver(forName:object:queue:using:) method returns an object which you should save in an instance property and pass to NotificationCenter.default.removeObserver in deinit.

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

But I would recommend using a different API so you don't have to write a deinit at all. Use Combine instead, like this:

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

This way, you don't have to write a deinit. When the ViewController is destroyed, it automatically destroys its instance properties, including the tickets array and its contents. When the ticket (AnyCancellable) returned by sink is destroyed, it cancels the subscription.

You might even be able to eliminate the use of self entirely. You didn't show the declaration of appName. If it's global, you can subscribe like this instead:

        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)

Now the closure captures view directly instead of capturing self, so there's no possibility of a retain cycle.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • I just start informing myself about `weak self` or `unowned self` or strong, what I found was that I do not need a strong self there because title has some old value there and my app would not crash if new title does not updated, also I do not think `unowned self` needed here because there is possibility that I never tap on button to update and it can be nil, so just `weak self` left to chose, is that true what I found? – ios coder Jan 31 '23 at 20:56
  • Also you said: "ensure that your observer is destroyed" if I want keep using my code in question and not using your answer could you add that part of code at your answer please as a reference for learning and correcting my codes? – ios coder Jan 31 '23 at 21:00
  • 1
    I added code to show how to destroy the observer in `deinit`. – rob mayoff Jan 31 '23 at 21:05
  • Thanks a lot I just like that you look my updated code for my question from your answer if it is also correct?, and I tried to trigger the `deinit` but I was unable to get that print! If I am unable to see that print so that method is not going called! I know that it is a must and needed method but what is the point if it does not triggered? at least I could not make it to get triggered! can you show me a way to see that `deinit` is going triggered? – ios coder Jan 31 '23 at 21:18