> On Jan 5, 2016, at 5:41 AM, Charles Srstka <cocoa...@charlessoft.com> wrote: > >> On Jan 4, 2016, at 10:32 PM, Douglas Gregor via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote: >> >> 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. > > 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.
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. > @implementation NSObject (Swizzle) 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. > + (void)load { > CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); > > unsigned int classCount = 0; > Class *classes = objc_copyClassList(&classCount); Doing it this way won’t handle protocol conformances or classes loaded later via a dlopen’d dylib. > > Protocol *proto = @protocol(HasSwiftExtension); > > for (unsigned int i = 0; i < classCount; i++) { > Class eachClass = classes[i]; > > if (class_conformsToProtocol(eachClass, proto)) { > unsigned int protoCount = 0; > Protocol * __unsafe_unretained *protocols = > class_copyProtocolList(eachClass, &protoCount); > > for (unsigned int j = 0; j < protoCount; j++) { > Protocol *eachProto = protocols[j]; > > if (protocol_conformsToProtocol(eachProto, proto)) { > unsigned int methodCount = 0; > // what we would want would be to pass YES for > isRequiredMethod; unfortunately, > // adding optional methods to an @objc protocol in an > extension currently just > // crashes the compiler when I try it. So pass NO, for > the demonstration. The crash is a bug; please file it. > struct objc_method_description *methods = > protocol_copyMethodDescriptionList(eachProto, NO, YES, &methodCount); > > for (unsigned int k = 0; k < methodCount; k++) { > struct objc_method_description method = methods[k]; > > if (!class_respondsToSelector(eachClass, > method.name)) { > [SwizzleWrapper swizzleClass:[eachClass class] > protocol:eachProto method:method]; > } > } > > free(methods); > } > } > > free(protocols); > } > } > > free(classes); > > NSLog(@"took %f seconds", CFAbsoluteTimeGetCurrent() - startTime); > } > @end > [snip] > (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.) 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. - Doug
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution