3

I am writing method which takes a type which conforms to a protocol and instantiates an instance of this class. When I build it, the compiler crashes with a segfault. I appreciate that this points to a compiler bug 99% of the time, but I am interested to see if what I'm trying to do is logically correct or am I just throwing absolute nonsense at the compiler and I shouldn't be surprised to see it crash.

Here is my code

protocol CreatableClass {
    init()
}

class ExampleClass : CreatableClass {
    required init() {

    }
}

class ClassCreator {
    class func createClass(classType: CreatableClass.Type) -> CreatableClass {
        return classType()
    }
}

ClassCreator.createClass(ExampleClass.self)

I also tried to rule out passing a Type as a method parameter as being the root of the problem and the following code also crashes the compiler:

protocol CreatableClass {
    init()
}

class ExampleClass : CreatableClass {
    required init() {

    }
}


let classType: CreatableClass.Type = CreatableClass.self
let instance = classType()

So - is this just a straightforward compiler bug and does what I am trying to do seem reasonable, or is there something in my implementation that is wrong?

Edit:

This can be achieved using generics as shown @Antonio below but unfortunately i believe that isn't useful for my application.

The actual non-dumbed down use-case for doing this is something like

protocol CreatableClass {}
protocol AnotherProtocol: class {}

class ClassCreator {
    let dictionary: [String : CreatableClass]

    func addHandlerForType(type: AnotherProtocol.Type, handler: CreatableClass.Type) {
        let className: String = aMethodThatGetsClassNameAsAString(type)
        dictionary[className] = handler()
    }

    required init() {}
}
Stephen Groom
  • 3,887
  • 1
  • 15
  • 23
  • For a somewhat extended discussion of a very similar problem, see http://stackoverflow.com/a/27574558/97337. The answer in that case was to pass closures that could construct what you needed. That said, in your specific case, if handler were just `CreatableClass` (rather than the type of the class) it would work. So I assume there's even more to the question than this. – Rob Napier Feb 03 '15 at 21:38

1 Answers1

2

I usually do that by defining a generic method. Try this:

class func createClass<T: CreatableClass>(classType: T.Type) -> CreatableClass {
    return classType()
}

Update

A possible workaround is to pass a closure creating a class instance, rather than passing its type:

class ClassCreator {
    class func createClass(instantiator: () -> CreatableClass) -> (CreatableClass, CreatableClass.Type) {
        let instance = instantiator()
        let classType = instance.dynamicType

        return (instance, classType)
    }
}

let ret = ClassCreator.createClass { ExampleClass() }

The advantage in this case is that you can store the closure in a dictionary for example, and create more instances on demand by just knowing the key (which is something in 1:1 relationship with the class name).

I used that method in a tiny dependency injection framework I developed months ago, which I realized it works only for @objc-compatible classes only though, making it not usable for my needs...

Antonio
  • 71,651
  • 11
  • 148
  • 165
  • On another day this would be the correct solution to my problem, but in my specific use case I can't use them. I attempted to dumb down my code as much as possible to make the question as simple as possible but the actual use case is a method that takes two types, instantiates one and uses another as the key for the instantiated class in a dictionary. I guess this is kinda relevant now we have generics though and I'll amend the question! – Stephen Groom Feb 03 '15 at 20:50
  • 1
    I don't see any other way of doing that in pure swift (hope to be wrong here) - you probably could use objc bridging, but at a price: you'd need to mark both protocol and concrete type as @objc, hence losing all swift specific features. – Antonio Feb 03 '15 at 21:16
  • Well I'm not entirely confident that it won't work but don't have access to the source code at present. I will know by tomorrow! See my Edit above if you're interested... – Stephen Groom Feb 03 '15 at 21:23
  • 1
    @StephenGroom Pls read updated answer - added another method - less elegant, but it might solve your problem – Antonio Feb 03 '15 at 21:39
  • 1
    This is the same place we got to the last time I saw this question. :D http://stackoverflow.com/a/27574558/97337 – Rob Napier Feb 03 '15 at 21:39