How to programatically remove a value transformer from a Binding in NSBindingOption

Viewed 43

I'm binding NSValueTransformer programatically to NSTextField property on demand in a macOS application. My question is how can I remove this binding?

Relevant code snippet to bind the NSValueTransformer:

@IBOutlet var plcAddrLabel: NSTextField!

@objc dynamic var registerAddr = Int(1)

var optionsDict: [NSBindingOption : Any] = [:]

optionsDict = [NSBindingOption.valueTransformer:decToHexTransformer(), NSBindingOption.valueTransformerName:NSValueTransformerName.decToHexTransformerName]
            
plcAddrLabel.bind(.value, to: self, withKeyPath: "plcRegisterAddress" , options: optionsDict)

When it comes to unbinding I have tried a number of different approaches:

The following code

optionsDict = [NSBindingOption.valueTransformer: "<null>", NSBindingOption.valueTransformerName: "<null>"]
plcAddrLabel.bind(.value, to: self, withKeyPath: "plcRegisterAddress" , options: optionsDict)

causes this error upon execution:

-[NSTaggedPointerString transformedValue:]: unrecognized selector sent to instance 0x7b73031cc07cbe65

The reason for using "<null>" is that it is returned by plcAddrLabel.infoForBinding(.value) before binding the NSValueTransformer for the first time:

NSValueTransformer = "<null>";

NSValueTransformerName = "<null>";

I have also tried

optionsDict = [NSBindingOption.valueTransformer: NSBindingOption.nullPlaceholder, NSBindingOption.valueTransformerName: NSBindingOption.nullPlaceholder]

unfortunately the result is the same.

Interestingly plcAddrLabel.infoForBinding(.value) also shows:

NSNullPlaceholder = "<null>";

It appears, as soon as I try to remove/clear the transformer option the transformer gets called.

Note: while the transformer is bound to the NSTextfield it works fine. Replacing one transformer with another transformer also works.

Any suggestions for further diagnosis would be much appreciated.

Xcode 13.2.1

2 Answers

I assume you wanted this

plcAddrLabel.unbind(.value)

I think you're looking or NSObject.unbind(_:)

It appears, as soon as I try to remove/clear the transformer option the transformer gets called. Note: while the transformer is bound to the NSTextfield it works fine.

Could you elaborate on this?

The following code

optionsDict = [NSBindingOption.valueTransformer: "<null>", NSBindingOption.valueTransformerName: "<null>"]
plcAddrLabel.bind(.value, to: self, withKeyPath: "plcRegisterAddress" , options: optionsDict)

causes this error upon execution:

Of course it does! "<null>" is just a string, with absolutely no special semantic value in this context. It's not an NSValueTransformer, and doesn't respond to the transformedValue message.


Side note, this code doesn't make much sense:

var optionsDict: [NSBindingOption : Any] = [:]

optionsDict = [NSBindingOption.valueTransformer:decToHexTransformer(), NSBindingOption.valueTransformerName:NSValueTransformerName.decToHexTransformerName]
            
plcAddrLabel.bind(.value, to: self, withKeyPath: "plcRegisterAddress" , options: optionsDict)

You're making an empty dictionary to assign to optionsDict, but you're then immediately replacing it with a new dictionary. This causes it to be needless mutable. It could just be:

let optionsDict: [NSBindingOption : Any] = [
    NSBindingOption.valueTransformer: decToHexTransformer(),
    NSBindingOption.valueTransformerName: NSValueTransformerName.decToHexTransformerName
]

plcAddrLabel.bind(.value, to: self, withKeyPath: "plcRegisterAddress" , options: optionsDict)

Better yet, if you inlined it, the type and used some implicit member expressions, it gets even nicer:

plcAddrLabel.bind(.value, to: self, withKeyPath: "plcRegisterAddress" , options: [
    .valueTransformer: decToHexTransformer(),
    .valueTransformerName: NSValueTransformerName.decToHexTransformerName
])
Related