<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=""><br class=""><div><blockquote type="cite" class=""><div class="">On Jan 5, 2016, at 5:41 AM, Charles Srstka <<a href="mailto:cocoadev@charlessoft.com" class="">cocoadev@charlessoft.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div 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 class=""><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></div></blockquote><div><br class=""></div><div>The compiler isn’t the limitation here, it’s the Objective-C runtime. That’s somewhat malleable, but making changes there to support a Swift feature affects backward deployment.</div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><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></div></div></blockquote><div><br class=""></div><div>Note that all approaches based on adding categories to a root class require you to enumerate root classes, as I noted in my original message. That’s unfortunate and requires more trickery.</div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><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="">void</span>)load {</div><div style="margin: 0px; line-height: normal; color: rgb(61, 29, 129);" class=""><span style="" class=""> </span><span style="font-variant-ligatures: no-common-ligatures; color: #703daa" class="">CFAbsoluteTime</span><span style="" class=""> startTime = </span>CFAbsoluteTimeGetCurrent<span style="" class="">();</span></div><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></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> 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></div></div></div></blockquote><div><br class=""></div><div>Doing it this way won’t handle protocol conformances or classes loaded later via a dlopen’d dylib.</div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><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; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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="" 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="" 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="" class=""> </span>// crashes the compiler when I try it. So pass NO, for the demonstration.</div></div></div></div></blockquote><div><br class=""></div><div>The crash is a bug; please file it.</div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><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="">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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><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><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; line-height: normal;" class=""> <span style="font-variant-ligatures: no-common-ligatures; color: #3d1d81" class="">free</span>(classes);</div><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; line-height: normal; color: rgb(61, 29, 129);" class=""><span style="" class=""> </span>NSLog<span style="" class="">(</span><span style="font-variant-ligatures: no-common-ligatures; color: #d12f1b" class="">@"took %f seconds"</span><span style="" class="">, </span>CFAbsoluteTimeGetCurrent<span style="" 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></div></blockquote><div><br class=""></div>[snip]</div><div><br class=""><blockquote type="cite" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div style="margin: 0px; line-height: normal;" class=""><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></div></blockquote></div><div><br class=""></div><div>There are better mechanisms for this than +load. But one would have to deal with the dylib loading issue and the need to enumerate root classes to get to a complete implementation. Frankly, I don’t think this level of Objective-C runtime hackery is worth the effort, hence my suggestion to make the existing behavior explicit.</div><div><br class=""></div><div><span class="Apple-tab-span" style="white-space:pre">        </span>- Doug</div><div><br class=""></div><br class=""></body></html>