> On Jan 5, 2016, at 5:41 AM, Charles Srstka <[email protected]> wrote:
>
>> On Jan 4, 2016, at 10:32 PM, Douglas Gregor via swift-evolution
>> <[email protected] <mailto:[email protected]>> 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
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution