1

when I use let UISwitch replace lazy var UISwitch in UITableViewCell like this:

let trigger: UISwitch = {
    let sw = UISwitch()
    sw.addTarget(self, action: #selector(onTriggerChanged(sender:)), for: .valueChanged)
    return sw
}()

I found the function onTriggerChanged never called, so I do some test, here is my test code:

class TestCell: UITableViewCell {

    lazy var trigger: UISwitch = {
        let sw = UISwitch()
        print("trigger \(self)")
        sw.addTarget(self, action: #selector(onTriggerChanged(sender:)), for: .valueChanged)
        return sw
    }()
    
    var trigger2: UISwitch = {
        let sw = UISwitch()
        print("trigger2 \(self)")
        sw.addTarget(self, action: #selector(onTriggerChanged(sender:)), for: .valueChanged)
        return sw
    }()

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        let trigger3: UISwitch = {
            let sw = UISwitch()
            print("trigger3 \(self)")
            sw.addTarget(self, action: #selector(onTriggerChanged(sender:)), for: .valueChanged)
            return sw
        }()

        contentView.addSubview(trigger)
        contentView.addSubview(trigger2)
        contentView.addSubview(trigger3)
    }


}

print message is:

trigger <TestCell: 0x......>

trigger2 (Function)

trigger3 <TestCell: 0x......>

and trigger2 valueChanged event not called.

so what difference of self in trigger/trigger2/trigger3

JoShin
  • 71
  • 3
  • 10
  • I get a warning that says "String interpolation produces a debug description for a function value; did you mean to make this explicit?" in trigger2, which makes it very clear what `self` means there. Do you not get one? – Sweeper Jul 01 '21 at 07:40

2 Answers2

2

I'm sure you're aware, self (in the usual sense) is not available in property initialisers of non-lazy properties, which is why you use lazy as a workaround.

The self that you are printing in trigger2 can't refer to the self instance. It actually refers to an instance method called self, declared in NSObject, which is inherited by TestCell.

This is actually an instance method. In Swift, you are allowed to refer to instance methods without having an instance (remember that you don't have access to the actual self instance here). It's just that their type becomes (T) -> F, where T is the type in which the instance method is declared, and F is the function type for the method. Essentially, they become static methods that take an instance as parameter, and give you back the original instance method.

For example, the self method, when referenced without an instance, would have the type:

(TestCell) -> (() -> TestCell)

You can see that is indeed what it is by printing out type(of: self).

Clearly, adding the self method as the target doesn't work. It can't respond to the selector onTriggerChangedWithSender.

For trigger1 and trigger3, the self instance is available, and self mean the usual thing - the current instance.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
0

Lazy var is computed when the variable is required somewhere and only runs the initializing code once while non lazy var and let compute the value when the variable is initialized. and where is the onTriggerChanged function.