I was thinking about button click events and we have different options
for a basic operation let's say
button.addTarget(self, action: #selector(buttonClicked), for: .touchUpInside)
@objc private func buttonClicked () {
print("button clicked")
}
however we can do the same thing without going through objc functions or selectors using the following
extension UIControl {
func addAction(for controlEvents: UIControl.Event = .touchUpInside, _ closure: @escaping()->()) {
addAction(UIAction { (action: UIAction) in closure() }, for: controlEvents)
}
}
button.addAction(for: .touchUpInside) { [weak self] in
guard let self = self else {return}
self.buttonClicked()
}
func settingsClicked () {
print("settings clicked")
}
I took a look at apple's documentation for addAction and could not find much there and I was wondering the difference between the two and which one should I use more
The addTarget(_:action:for:)
approach uses target/action, the old Objective-C based dynamic dispatch approach where you provide a target object and a selector.
If you are targeting iOS ≥14, you can use the newer addAction(_:for:)
which allows you to provide a UIAction
object, which includes a closure. It's more modern, but won't work for iOS <14.
Closures and memory:
One thing to be aware of with closures is that there is the potential to create a "retain cycle" and possible memory leak.
If your closure references instance variables from self
, that causes the closure to hold a strong reference to self.
IBAction closures are "escaping" closures, which means that they persist. The system retains the closure, and the closure can retain the object that defines it (often a view controller.) That can lead to retain cycles and memory leaks.
The way to avoid closures capturing objects is with a "capture group". If your closure is retaining "self", you'd add a capture group that causes self to be referenced weakly. ([weak self]
)
That might look like this:
let action = UIAction(title: "Tap Me", handler: { [weak self] theAction in
print("Tapped on \(String(describing: theAction.sender))");
self?.doSomething(theAction); // Self is now optional and won't be retained.
});
let button = UIButton(configuration: .filled(), primaryAction: action);