Workaround to bridge a *generic* protocol to Objective-C? - swift

Let's say that we have the following Objective-C API:
- (id)foo:(Protocol *)proto;
Which is imported into Swift as:
func foo(_ proto: Protocol) -> Any
Yep, it's one of those things that gives us a proxy object. These tend to be annoying to use in Swift, so let's say we want to make a wrapper around this thing to make it a bit friendlier. First we define a couple of Objective-C-compatible protocols:
#objc protocol Super {}
#objc protocol Sub: Super {}
Now, we define a function that takes a protocol conforming to Super and passes it along to foo(), and then we call it with Sub as the parameter to see if it works:
func bar<P: Super>(proto: P.Type) {
let proxy = foo(proto)
// do whatever with the proxy
}
bar(proto: Sub.self)
Well, this doesn't compile. The error message given is:
error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
Here's some stuff that does (mostly) compile:
func bar<P: Super>(proto: P.Type) {
// when called with 'Sub.self' as 'proto':
print(type(of: proto)) // Sub.Protocol
print(type(of: Sub.self)) // Sub.Protocol
print(proto == Sub.self) // true
let proxy1 = foo(Sub.self) // compiles, runs, works
let proxy2 = foo(proto) // error: cannot convert value of type 'P.Type' to expected argument type 'Protocol'
}
Okay, it's the same as Sub.self in almost every way except that I can't pass it to something requiring an Objective-C protocol. Hmm.
The problem is that, although the fact that Sub conforms to Super means that it must be an Objective-C protocol, the compiler isn't realizing this. Can we work around that and get it bridged manually? Well, let's take a look at Protocol's interface, to see if there's anything that we can...
OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0)
#interface Protocol : NSObject
#end
Oh. Hrm.
Well, the fact that Protocol is a full-fledged NSObject subclass suggests that this is all probably the work of my bestest favoritest feature, the magical Swift<->Objective-C bridge that performs non-trivial conversions on things without it being obvious what is going on. Well, this gives me an idea, at least; I should be able to manually invoke the bridge by casting to AnyObject and hopefully get at the Protocol object that way maybe by as!ing it or something. Does it work?
print(Sub.self as AnyObject) // <Protocol: 0x012345678>
Well, that's promising. And when I try it on my generic parameter?
print(proto as AnyObject) // Terminated due to signal: SEGMENTATION FAULT (11)
Oh, come on.
I suspect this is probably a bug in the compiler, and I plan to test a few things to determine whether that's the case, but since the Swift sources take a geologic age to compile, I figured I'd post this here while I'm waiting. Anyone have any insight and/or workarounds on what is going on here?

I know it's not pretty and "Swifty", exactly, but getting the Objective-C Protocol as appropriate for passing into foo can be achieved using NSProtocolFromString as follows:
let proxy2 = foo(NSProtocolFromString(String(reflecting: proto))!)
where String(reflecting:) is a useful way to get the fully qualified type name suitable for resolving via NSProtocolFromString.
I'd say the crash you encountered is a bug.

Okay, after investigating this a bit more, I've determined that it is indeed a compiler bug, and have filed a report on it: SR-8129. What appears to be happening is that the Swift compiler falsely assumes that proto will always be a metatype of a concrete class type, so it performs the bridging by emitting a call to swift_getObjCClassFromMetadata, which crashes when it encounters the protocol metatype. When Sub.self is explicitly cast to AnyObject, the compiler emits Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject instead, which appears to dynamically determine the type of the object and bridge it accordingly.
With this in mind, the workaround becomes apparent: cast proto to Any first, to destroy the type information associated with the generic and force the compiler to emit Swift._bridgeAnythingToObjectiveC<A>(A) -> Swift.AnyObject. And indeed, this appears to work:
func bar<P: Super>(proto: P.Type) {
foo(proto as Any as AnyObject as! Protocol)
}
Not the prettiest thing out there, but probably preferable to looking up the protocol via string manipulation.

Related

Differences generic protocol type parameter vs direct protocol type

This is my playground code:
protocol A {
init(someInt: Int)
}
func direct(a: A) {
// Doesn't work
let _ = A.init(someInt: 1)
}
func indirect<T: A>(a: T) {
// Works
let _ = T.init(someInt: 1)
}
struct B: A {
init(someInt: Int) {
}
}
let a: A = B(someInt: 0)
// Works
direct(a: a)
// Doesn't work
indirect(a: a)
It gives a compile time error when calling method indirect with argument a. So I understand <T: A> means some type that conforms to A. The type of my variable a is A and protocols do not conform to themselfs so ok, I understand the compile time error.
The same applies for the compile time error inside method direct. I understand it, a concrete conforming type needs to inserted.
A compile time also arrises when trying to access a static property in direct.
I am wondering. Are there more differences in the 2 methods that are defined? I understand that I can call initializers and static properties from indirect and I can insert type A directly in direct and respectively, I can not do what the other can do. But is there something I missed?
The key confusion is that Swift has two concepts that are spelled the same, and so are often ambiguous. One of the is struct T: A {}, which means "T conforms to the protocol A," and the other is var a: A, which means "the type of variable a is the existential of A."
Conforming to a protocol does not change a type. T is still T. It just happens to conform to some rules.
An "existential" is a compiler-generated box the wraps up a protocol. It's necessary because types that conform to a protocol could be different sizes and different memory layouts. The existential is a box that gives anything that conforms to protocol a consistent layout in memory. Existentials and protocols are related, but not the same thing.
Because an existential is a run-time box that might hold any type, there is some indirection involved, and that can introduce a performance impact and prevents certain optimizations.
Another common confusion is understanding what a type parameter means. In a function definition:
func f<T>(param: T) { ... }
This defines a family of functions f<T>() which are created at compile time based on what you pass as the type parameter. For example, when you call this function this way:
f(param: 1)
a new function is created at compile time called f<Int>(). That is a completely different function than f<String>(), or f<[Double]>(). Each one is its own function, and in principle is a complete copy of all the code in f(). (In practice, the optimizer is pretty smart and may eliminate some of that copying. And there are some other subtleties related to things that cross module boundaries. But this is a pretty decent way to think about what is going on.)
Since specialized versions of generic functions are created for each type that is passed, they can in theory be more optimized, since each version of the function will handle exactly one type. The trade-off is that they can add code-bloat. Do not assume "generics are faster than protocols." There are reasons that generics may be faster than protocols, but you have to actually look at the code generation and profile to know in any particular case.
So, walking through your examples:
func direct(a: A) {
// Doesn't work
let _ = A.init(someInt: 1)
}
A protocol (A) is just a set of rules that types must conform to. You can't construct "some unknown thing that conforms to those rules." How many bytes of memory would be allocated? What implementations would it provide to the rules?
func indirect<T: A>(a: T) {
// Works
let _ = T.init(someInt: 1)
}
In order to call this function, you must pass a type parameter, T, and that type must conform to A. When you call it with a specific type, the compiler will create a new copy of indirect that is specifically designed to work with the T you pass. Since we know that T has a proper init, we know the compiler will be able to write this code when it comes time to do so. But indirect is just a pattern for writing functions. It's not a function itself; not until you give it a T to work with.
let a: A = B(someInt: 0)
// Works
direct(a: a)
a is an existential wrapper around B. direct() expects an existential wrapper, so you can pass it.
// Doesn't work
indirect(a: a)
a is an existential wrapper around B. Existential wrappers do not conform to protocols. They require things that conform to protocols in order to create them (that's why they're called "existentials;" the fact that you created one proves that such a value actually exists). But they don't, themselves, conform to protocols. If they did, then you could do things like what you've tried to do in direct() and say "make a new instance of an existential wrapper without knowing exactly what's inside it." And there's no way to do that. Existential wrappers don't have their own method implementations.
There are cases where an existential could conform to its own protocol. As long as there are no init or static requirements, there actually isn't a problem in principle. But Swift can't currently handle that. Because it can't work for init/static, Swift currently forbids it in all cases.

In swift, why can't I instantiate a protocol when it has an initialiser?

I understand that generally I cannot instantiate a protocol.
But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use?
My code is as below and line:
protocol Solution {
var answer: String { get }
}
protocol Problem {
var pose: String { get }
}
protocol SolvableProblem: Problem {
func solve() -> Solution?
}
protocol ProblemGenerator {
func next() -> SolvableProblem
}
protocol Puzzle {
var problem: Problem { get }
var solution: Solution { get }
init(problem: Problem, solution: Solution)
}
protocol PuzzleGenerator {
func next() -> Puzzle
}
protocol FindBySolvePuzzleGenerator: PuzzleGenerator {
var problemGenerator: ProblemGenerator { get }
}
extension FindBySolvePuzzleGenerator {
func next() -> Puzzle {
while true {
let problem = problemGenerator.next()
if let solution = problem.solve() {
return Puzzle(problem: problem, solution: solution)
}
}
}
}
The line:
return Puzzle(problem: problem, solution: solution)
gives error: Protocol type 'Puzzle' cannot be instantiated
Imagine protocols are adjectives. Movable says you can move it, Red says it has color = "red"... but they don't say what it is. You need a noun. A Red, Movable Car. You can instantiate a Car, even when low on details. You cannot instantiate a Red.
But if I include an initialiser in the protocol then surely the compiler knows that when the protocol is used by a struct or class later, it will have an init which it can use?
Protocols must be adopted by classes, and there might be a dozen different classes that all adopt your Puzzle protocol. The compiler has no idea which of those classes to instantiate.
Protocols give us the power to compose interfaces without the complexity of multiple inheritance. In a multiple inheritance language like C++, you have to deal with the fact that a single class D might inherit from two other classes, B and C, and those two classes might happen to have methods or instance variables with the same name. If they both have a methodA(), and B::methodA() and C::methodA() are different, which one do you use when someone call's D's inherited methodA()? Worse, what if B and C are both derived from a common base class A? Protocols avoid a lot of that by not being directly instantiable, while still providing the interface polymorphism that makes multiple inheritance attractive.
I understand that I can't do it - I just want to understand why the
compiler can't do it?
Because protocols in Swift represent abstraction mechanism. When it comes to abstraction, you could think about it as a template, we don't have to care about the details of how it behaves or what's its properties; Thus it makes no sense to be able to create an object from it.
As a real world example, consider that I just said "Table" (as an abstracted level), I would be pretty sure that you would understand what I am talking about! nevertheless we are not mentioning details about it (such as its material or how many legs it has...); At some point if I said "create a table for me" (instantiate an object) you have the ask me about specs! and that's why the complier won't let you create object directly from a protocol. That's the point of making things to be abstracted.
Also, checking: Why can't an object of abstract class be created? might be helpful.
Unfortunately swift does not allow that even with such "hack"
You would need to use a class that confirms to that protocol as an object you refer to.
When you instantiate an object, the Operating System has to know how to allocate and deal with that kind of object in the memory: Is it a reference type (Classes)? Strong, weak or unowned reference? Or is it a value type (Structs, Strings, Int, etc)?
Reference types are stored in the Heap, while value types live in the Stack. Here is a thorough explanation of the difference between the two.
Only Reference and Value types (objects) can be instantiated. So, only the objects that conform to that protocol can then be instantiated, not the protocol itself. A protocol is not an object, it is a general description or schema of a certain behavior of objects.
As to Initialization, here what the Apple docs say:
Initialization is the process of preparing an instance of a class,
structure, or enumeration for use. This process involves setting an
initial value for each stored property on that instance and performing
any other setup or initialization that is required before the new
instance is ready for use.

How can I call NSStringFromProtocol with a Swift protocol?

Assuming some
protocol MyCoolProtocol {
....
}
the following code refuses to compile (as of Swift 2.1):
let protocolName = NSStringFromProtocol(MyCoolProtocol)
because MyCoolProtocol is not of type Protocol. (This, at first glance, seems really weird, but if you dig enough it [unfortunately] makes sense)
How can I get the name of my Swift protocol in a String?
There are two ways:
The most common suggestion I can find is to declare your protocol as #objc. This seems odd when you have no intention of referring to this protocol from Objective-C code.
You can use let protocolName = String(MyCoolProtocol). As of the current version of Swift, this gives exactly what you'd expect ("MyCoolProtocol") and is still checked at compile-time.
This is how I accomplished it:
let protocolString = String("\(MyProtocol.self)")

How to use a protocol with optional class methods in an extension with generic in Swift?

I am trying using extension for an existing class with class method like:
#objc public protocol MyProtocol {
optional class func foo() -> Int
}
And I am using this protocol in an extension with generic like:
extension MyClass {
public func bar<T: MyProtocol>() {
...
let x: Int = T.self.foo!() // if I do not use "self" or "!" here, I will have a compiler error
...
}
This should work but when I build it, Xcode says "Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1". If I do not use "optional" in the protocol, I do not need to unwrap the foo() in the extension and everything works well even if I remove the "self". Can anyone tell me why and how to make the optional work properly?
Thanks in advance.
It looks like you've found a (fairly obscure) bug in the Swift compiler that's causing it to crash.
Here's a reproduction that's all you need in a single file to crash swiftc:
import Foundation
#objc protocol P { optional class func f() }
func f<T: P>(t: T) { T.self.f?() }
(you don't need to call f for it to crash)
You should probably file a radar since the compiler crashing is never expected behavior no matter what your code.
If you tried to do this without the optional, it'll work (and you can even ditch the self). My guess is the implementation of generics doesn't currently account for the possibility of optional class-level functions.
You can do it without generics like this:
func f(p: MyProtocol) {
(p as AnyObject).dynamicType.foo?()
}
(there may even be a better way but I can't spot it).
You need the AnyObject cast because if you try to call .dynamicType.foo?() on p directly you get "accessing members of protocol type value 'MyProtocol.Type' is unimplemented". I wouldn't be surprised if the crash of the generic version is related to this.
I'd also say that its worth asking yourself whether you really need a protocol with optional methods (especially class-level ones) and whether there's a way to do what you want entirely statically using generics (as you're already semi-doing).

What is ExistentialMetatype in Swift

This wonders me a little bit. In Swift try the following:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println(self.superclass) //Outputs "(ExistentialMetatype)"
}
This gives the output on console to be ExistentialMetatype
Going deep into the Swift's NSObject framework it declares this superclass as a read-only computed property of type AnyClass
var superclass: AnyClass! { get }
But nowhere is there a mention of this word ExistentialMetatype. I don't know why but it reminds of Objective-C's runtime (probably its the word Metatype in it). Does anyone know anything about it?
Currently, types (instances of metatypes) get printed as unhelpful things like (Metatype) or (ExistentialMetatype). AnyClass is AnyObject.Type, a metatype. So if you try to print an AnyClass you will get this unhelpful description.
However, classes in Objective-C are objects with nice descriptions. Swift allows casting of class types into objects so they can be used in the Objective-C way. If you cast it into AnyObject first, you will see the name of the class:
println(self.superclass as AnyObject!)
If I recall correctly, what is going on is that AnyClass is a specially crafted protocol type - the protocol type that every "reference type" (class) automagically conforms to
Protocol types are sometimes called "existential types" by your friendly compiler engineers (for instance http://www.haskell.org/haskellwiki/Existential_type)
In this case, you're looking at the type object that represents such an existential type, the "metatype" as you say. Hence 'ExistentialMetatype'!