<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><blockquote type="cite" class="">On Jan 4, 2016, at 10:32 PM, Douglas Gregor via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:<br class=""></blockquote><div><blockquote type="cite" class=""><br class="Apple-interchange-newline"><div class=""><span style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">There is no direct way to implement Objective-C entry points for protocol extensions. One would effectively have to install a category on every Objective-C root class [*] with the default implementation or somehow intercept all of the operations that might involve that selector. </span></div></blockquote></div><br class=""><div class="">I can almost do it right now, just hacking with the Objective-C runtime functions, so I’d think that if you were actually working with the compiler sources, it should be doable. The trouble is on the Swift side; currently there aren’t any reflection features that I can find that work on Swift protocols.</div><div class=""><br class=""></div><div class="">If I have a protocol and class, like so:</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">import</span> Foundation</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">@objc</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">protocol</span> HasSwiftExtension {}</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(79, 129, 135);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">@objc</span><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">protocol</span><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> P: </span>HasSwiftExtension<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> {</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(187, 44, 162);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>optional<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>func<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> foo()</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class="">}</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(187, 44, 162);" class="">extension<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span><span style="font-variant-ligatures: no-common-ligatures; color: #4f8187" class="">P</span><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> {</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">func</span> foo() { <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">print</span>(<span style="font-variant-ligatures: no-common-ligatures; color: #d12f1b" class="">"foo"</span>) }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class="">}</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">class</span> C: <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">NSObject</span>, <span style="font-variant-ligatures: no-common-ligatures; color: #4f8187" class="">P</span> {}</div></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">(the optional is there because without it, adding the method in an extension causes the compiler to crash on my machine)</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">And then I have this in Objective-C:</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal;" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><div style="margin: 0px; line-height: normal;" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">@implementation</span> NSObject (Swizzle)</div><div style="margin: 0px; line-height: normal;" class="">+ (<span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">void</span>)load {</div><div style="margin: 0px; line-height: normal; color: rgb(61, 29, 129);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span><span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">CFAbsoluteTime</span><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> startTime = </span>CFAbsoluteTimeGetCurrent<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class="">();</span></div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">unsigned</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">int</span> classCount = <span style="font-variant-ligatures: no-common-ligatures; color: #272ad8" class="">0</span>;</div><div style="margin: 0px; line-height: normal;" class=""> Class *classes = <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">objc_copyClassList</span>(&classCount);</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> Protocol *proto = <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">@protocol</span>(<span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">HasSwiftExtension</span>);</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">for</span> (<span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">unsigned</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">int</span> i = <span style="font-variant-ligatures: no-common-ligatures; color: #272ad8" class="">0</span>; i < classCount; i++) {</div><div style="margin: 0px; line-height: normal;" class=""> Class eachClass = classes[i];</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">if</span> (<span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">class_conformsToProtocol</span>(eachClass, proto)) {</div><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">unsigned</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">int</span> protoCount = <span style="font-variant-ligatures: no-common-ligatures; color: #272ad8" class="">0</span>;</div><div style="margin: 0px; line-height: normal;" class=""> Protocol * <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">__unsafe_unretained</span> *protocols = <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">class_copyProtocolList</span>(eachClass, &protoCount);</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">for</span> (<span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">unsigned</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">int</span> j = <span style="font-variant-ligatures: no-common-ligatures; color: #272ad8" class="">0</span>; j < protoCount; j++) {</div><div style="margin: 0px; line-height: normal;" class=""> Protocol *eachProto = protocols[j];</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">if</span> (<span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">protocol_conformsToProtocol</span>(eachProto, proto)) {</div><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">unsigned</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">int</span> methodCount = <span style="font-variant-ligatures: no-common-ligatures; color: #272ad8" class="">0</span>;</div><div style="margin: 0px; line-height: normal; color: rgb(0, 132, 0);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>// what we would want would be to pass YES for isRequiredMethod; unfortunately,</div><div style="margin: 0px; line-height: normal; color: rgb(0, 132, 0);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>// adding optional methods to an @objc protocol in an extension currently just</div><div style="margin: 0px; line-height: normal; color: rgb(0, 132, 0);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>// crashes the compiler when I try it. So pass NO, for the demonstration.</div><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">struct</span> <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">objc_method_description</span> *methods = <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">protocol_copyMethodDescriptionList</span>(eachProto, <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">NO</span>, <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">YES</span>, &methodCount);</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">for</span> (<span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">unsigned</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">int</span> k = <span style="font-variant-ligatures: no-common-ligatures; color: #272ad8" class="">0</span>; k < methodCount; k++) {</div><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">struct</span> <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">objc_method_description</span> method = methods[k];</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">if</span> (!<span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">class_respondsToSelector</span>(eachClass, method.<span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">name</span>)) {</div><div style="margin: 0px; line-height: normal;" class=""> [<span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">SwizzleWrapper</span> <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">swizzleClass</span>:[eachClass <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">class</span>] <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">protocol</span>:eachProto <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">method</span>:method];</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">free</span>(methods);</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">free</span>(protocols);</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">free</span>(classes);</div><p style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; line-height: normal; color: rgb(61, 29, 129);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>NSLog<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class="">(</span><span style="font-variant-ligatures: no-common-ligatures; color: #d12f1b" class="">@"took %f seconds"</span><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class="">, </span>CFAbsoluteTimeGetCurrent<span style="font-variant-ligatures: no-common-ligatures; color: #000000" class="">() - startTime);</span></div><div style="margin: 0px; line-height: normal;" class="">}</div><div style="margin: 0px; line-height: normal; color: rgb(187, 44, 162);" class="">@end</div></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">The swizzleClass:protocol:method: method will get called for each missing method, assuming I’ve marked the protocols having an extension by making them conform to my HasSwiftExtension protocol, which the compiler could add automatically. (For the record, the time taken was 0.001501 seconds in my testing, while linking against both Foundation and AppKit).</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">Unfortunately there’s currently no way to go any further, since AFAIK there’s no way to reflect on a protocol to get a mapping from selector name to method. For this to work, you’d have to store the method names for methods added by extensions to @objc protocols as strings somewhere, and then have a reflection API to access them. However, if you added that, you could just:</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">class</span> SwizzleWrapper: <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">NSObject</span> {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">class</span> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">func</span> swizzleClass(aClass: <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">AnyClass</span>, `protocol` aProto: <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">Protocol</span>, method: <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">objc_method_description</span>) {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #bb2ca2" class="">let</span> imp: <span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">IMP</span></div><p style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(0, 132, 0);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>// now, just add some reflection for protocols to the language so we can</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(0, 132, 0);" class=""><span style="font-variant-ligatures: no-common-ligatures; color: #000000" class=""> </span>// figure out what method to call and set imp accordingly, and:</div><p style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></p><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">class_addMethod</span>(aClass, method.<span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">name</span>, imp, method.<span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">types</span>) <span style="font-variant-ligatures: no-common-ligatures; color: #008400" class="">// ta da!</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class="">}</div><div class=""><br class=""></div><div class="">The other obvious disclaimer, of course, is that +load is probably not the right place to do this; you’d need to set things up such that they would run sometime after the Swift runtime has had a chance to finish initializing; the code as above probably isn’t safe if the Swift method being called actually does anything. But with access to the compiler source, you could make sure to get the SetUpStuff() method to run at the appropriate time, so that it could call into Swift and do its setup.</div></div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">(For the record, I’m not advocating actually using the swizzling method described above; just pointing out that intercepting the selector is possible. Working with the compiler sources, I’d expect more elegant solutions would be possible.)</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">Charles</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div></div></body></html>