<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"><channel><title>mikeash.com pyblog/friday-qa-2015-12-25-swifty-targetaction.html comments</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>mikeash.com Recent Comments</description><lastBuildDate>Sun, 12 Apr 2026 02:09:05 GMT</lastBuildDate><generator>PyRSS2Gen-1.0.0</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>overcyn - 2017-02-14 22:45:12</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>AppKit version updated for Swift 3... 
&lt;br /&gt;
&lt;br /&gt;&lt;code&gt;
&lt;br /&gt;let NSControlActionFunctionProtocolAssociatedObjectKey = UnsafeMutablePointer&amp;lt;Int8&amp;gt;.allocate(capacity:1)
&lt;br /&gt;
&lt;br /&gt;class ActionTrampoline&amp;lt;T&amp;gt;: NSObject {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var action: (T) -&amp;gt; Void
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init(action: @escaping (T) -&amp;gt; Void) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.action = action
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@objc func action(_ sender: NSControl) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;action(sender as! T)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;protocol NSControlActionFunctionProtocol {}
&lt;br /&gt;
&lt;br /&gt;extension NSControlActionFunctionProtocol where Self: NSControl {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func setAction(action: @escaping (Self) -&amp;gt; Void) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let trampoline = ActionTrampoline(action: action)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.target = trampoline
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.action = Selector("action:")
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;objc_setAssociatedObject(self, NSControlActionFunctionProtocolAssociatedObjectKey, trampoline, .OBJC_ASSOCIATION_RETAIN)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;extension NSControl: NSControlActionFunctionProtocol {}
&lt;br /&gt;&amp;lt;\code&amp;gt;&lt;/code&gt;</description><guid isPermaLink="true">c0c522775891011aa172ab7b4fa37ff4</guid><pubDate>Tue, 14 Feb 2017 22:45:12 GMT</pubDate></item><item><title>sveinhal - 2016-01-04 08:56:08</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>One would also have to make sure to not reference the control from within the action closure, but use the passed argument instead. It is tempting to just use
&lt;br /&gt;
&lt;br /&gt;&lt;code&gt;button.setAction { _ in
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print("Action from: \(button.title)")
&lt;br /&gt;}
&lt;br /&gt;&lt;/code&gt;
&lt;br /&gt;… rather than
&lt;br /&gt;
&lt;br /&gt;&lt;code&gt;button.setAction { button in
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print("Action from: \(button.title)")
&lt;br /&gt;}
&lt;br /&gt;&lt;/code&gt;
&lt;br /&gt;And since the control holds a strong reference to the trampoline (through the associated object mechanism) and the trampoline may hold a strong reference to the control, by referencing the variable from the surrounding scope in the closure, this easily leads to retain cycles as well.</description><guid isPermaLink="true">cfe8d483304ce6760f5fb74010c42c20</guid><pubDate>Mon, 04 Jan 2016 08:56:08 GMT</pubDate></item><item><title>Jessy - 2015-12-28 21:35:20</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>I believe this to be very complicated. Instead, I store a weak-referencing "MultiClosure", and have each control be its own target.
&lt;br /&gt;
&lt;br /&gt;Example usage: 
&lt;br /&gt;textField.editingDidEndOnExit📡 += onEditingDidEndOnExit
&lt;br /&gt;
&lt;br /&gt;Where onEditingDidEndOnExit  is an "EquatableClosure".
&lt;br /&gt;
&lt;br /&gt;Hit me up @jessyMeow on Twitter if interested.</description><guid isPermaLink="true">5bc940acd0ea7c87cd65aba7a3f908e2</guid><pubDate>Mon, 28 Dec 2015 21:35:20 GMT</pubDate></item><item><title>Stephen Celis - 2015-12-26 19:31:46</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>There was an early discussion on swift-evolution about accepting closures wherever selectors appear. Joe Groff had an idea to address retain cycle concerns by using something like &lt;code&gt;@convention(objc_selector)&lt;/code&gt; to enforce a context-free closure:
&lt;br /&gt;
&lt;br /&gt;&lt;a href="https://lists.swift.org/pipermail/swift-evolution/2015-December/000200.html"&gt;https://lists.swift.org/pipermail/swift-evolution/2015-December/000200.html&lt;/a&gt;</description><guid isPermaLink="true">4b10ec6998fb03d51053db059df520d5</guid><pubDate>Sat, 26 Dec 2015 19:31:46 GMT</pubDate></item><item><title>J - 2015-12-26 14:15:34</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>Max O: but now you're just back to the existing Cocoa target-action API...</description><guid isPermaLink="true">acaf13b587876eecbd63d5f3af826c15</guid><pubDate>Sat, 26 Dec 2015 14:15:34 GMT</pubDate></item><item><title>Max O - 2015-12-26 00:08:01</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>There is a clear practical reason why original target-action pattern implementation creates &lt;b&gt;weak&lt;/b&gt; relationship. But when converted to block-based API this weak semantics is no longer enforced by the API and becomes responsibility of the programmer which is bad. And implicit capture semantics of blocks/closures makes it hard to maintain and error-prone in practice – as per your note on retain cycles.
&lt;br /&gt;&amp;nbsp;
&lt;br /&gt;Given that we can solve 'stringly-typed' problem but still force weak relationship semantics:
&lt;br /&gt;&lt;code&gt;
&lt;br /&gt;class ActionTrampoline&amp;lt;T: AnyObject, C&amp;gt;: NSObject {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;weak var target: T?
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;var method: T -&amp;gt; C -&amp;gt; Void
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let associatedObjectKey = UnsafeMutablePointer&amp;lt;Int8&amp;gt;.alloc(1)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init(target: T, method: T -&amp;gt; C -&amp;gt; Void) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.target = target
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.method = method
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@objc func action(sender: UIControl) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if let target = target {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;method(target)(sender as! C)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;protocol NSControlActionFunctionProtocol {}
&lt;br /&gt;extension NSControlActionFunctionProtocol where Self: UIControl {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func addTarget&amp;lt;T: AnyObject&amp;gt;(target: T, method: T -&amp;gt; Self -&amp;gt; Void, events: UIControlEvents) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let trampoline = ActionTrampoline(target: target, method: method)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.addTarget(trampoline, action: "action:", forControlEvents: events)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;objc_setAssociatedObject(self, trampoline.associatedObjectKey, trampoline, .OBJC_ASSOCIATION_RETAIN)
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;extension UIControl: NSControlActionFunctionProtocol {}
&lt;br /&gt;
&lt;br /&gt;class Controller {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;let button = UIButton()
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init() {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;button.addTarget(self, method: self.dynamicType.handleButton, events: [.TouchUpInside])
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;func handleButton(button: UIButton) {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print("Action from \(button.titleLabel?.text)")
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&lt;br /&gt;}
&lt;br /&gt;
&lt;br /&gt;let controller = Controller()
&lt;br /&gt;controller.button.sendActionsForControlEvents([.TouchUpInside])
&lt;br /&gt;&lt;/code&gt;</description><guid isPermaLink="true">384f6874ab19fa3a2d2d88dcb911902a</guid><pubDate>Sat, 26 Dec 2015 00:08:01 GMT</pubDate></item><item><title>Tobol - 2015-12-25 21:52:17</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>Hello Mike! Great article as always.
&lt;br /&gt;
&lt;br /&gt;I think you have a bug in the UIKit version. You are always setting the associated object for the same key. If you try to assign an action for another event, target for the previous one will be deallocated:
&lt;br /&gt;&lt;code&gt;
&lt;br /&gt;let button = UIButton()
&lt;br /&gt;button.setTitle("my button", forState: .Normal)
&lt;br /&gt;button.addAction([.TouchUpInside], {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print("TouchUpInside action from \($0.titleLabel?.text)")
&lt;br /&gt;})
&lt;br /&gt;button.addAction([.TouchDown], {
&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print("TouchDown action from \($0.titleLabel?.text)")
&lt;br /&gt;})
&lt;br /&gt;button.sendActionsForControlEvents([.TouchUpInside])
&lt;br /&gt;button.sendActionsForControlEvents([.TouchDown])
&lt;br /&gt;
&lt;br /&gt;Output:
&lt;br /&gt;TouchDown action from Optional("my button")
&lt;br /&gt;&lt;/code&gt;</description><guid isPermaLink="true">86b418d94b1cb6f83d0ad8428c15cd20</guid><pubDate>Fri, 25 Dec 2015 21:52:17 GMT</pubDate></item><item><title>Alex - 2015-12-25 18:28:48</title><link>http://www.mikeash.com/?page=pyblog/friday-qa-2015-12-25-swifty-targetaction.html#comments</link><description>I think you can safely use `&amp;amp;` operator for keys, Apple uses it for KVO (note "global context variable"): &lt;a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12"&gt;https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12&lt;/a&gt;</description><guid isPermaLink="true">abe344725ec04e6ddfb0374d462fa202</guid><pubDate>Fri, 25 Dec 2015 18:28:48 GMT</pubDate></item></channel></rss>
