Search code examples
swiftmacosselectornsbutton

NSButtion action not called when target is different object


I've got an NSButton in a View Controller that, when clicked, should call a method in an instance of another class (I have that instance in the View Controller). However, the action method is never called.

My code is below (it's short and simple). Please can somebody explain why this is?

View Controller class with the button:

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
        self.view.addSubview(b)

        let g = Global()

        b.target = g
        b.action = #selector(g.s)
    }
}

Class called 'Global' that I create an instance of, that the button should then call a method within:

class Global:NSObject {

    override init() {
        super.init()
    }

    @objc dynamic func s() {
        Swift.print("S ran")
    }
}

Thanks

Update: For easy reproduction, I've created a GitHub repo showing the issue in its simplest form here.


Solution

  • The problem is that by the time you click the button, target has been set to nil. This is because g is stored as a local variable and target is a weak property, so after viewDidLoad is finished, g is released and the target becomes nil. So, by the time you click the button, there is no object on which to call the action.

    You need to store a strong reference to the target somewhere. One way would be to store it as an instance variable on your view controller:

    class ViewController: NSViewController {
        let g = Global()
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let b:NSButton = NSButton(frame: NSRect(x: 150, y: 200, width: 30, height: 30))
            self.view.addSubview(b)
    
            b.target = g
            b.action = #selector(g.s)
        }
    }