Swift Generic Factory: Bug

Swift Generic Factory: Bug?

You have two issues here – one is incorrect code, the other is a known bug (my radar of it was closed as a dup of 18518629 which is still open as of 1.2b4).

Firstly, in this construction:

class func makeInstance< T : BaseClass >( type: AnyClass ) -> T?
{
return T()
}

// then later

BaseClassFactory.makeInstance( DerivedClass.Type )

your argument is not doing anything. It's essentially pointless and not contributing to the type of T (how could it? the argument does not reference T). Instead, the type of T will be chosen from context i.e. if you assign the result to a DerivedClass variable, T will be DerivedClass. If you don't specify, the default behavior is to make T the base class it's constrained to i.e. BaseClass.

What you probably mean is this:

class func makeInstance< T : BaseClass >( type: T.Type ) -> T?
{
return T()
}

// then later

BaseClassFactory.makeInstance( DerivedClass.self )

This should do the trick of setting T to be the type you want. Except it still won't work, because of a bug caused by the base class not having a dynamically-dispatched initializer (there is only a single runtime implementation of the generic and it relies on the right init being called polymorphically).

If you add required init() { } to BaseType you'll get the correct behaviour.

Swift/iOS SDK: Generic Function with Class Type/Name Closure/Block Issue

The correct way to pass a Type is ClassName.self, e.g.

create(A.self)
create(B.self, addParameter: { (b: B) -> Void in
b.test3 = 1000
})

(and I cannot tell you why omitting .self works if the function
is called without additional arguments).

With this correction, your code compiles without errors, but does
not work. As noticed in Swift Generic Factory: Bug?,
you have to add an required init method to your base class,

class Base {
required init() { } // ***
var test1: Int = 0
}

otherwise

var object = aClass()

will always create an object of the base class and not of the actual
type what was passed as an argument (and therefore b.test3 = 1000
in the closure will abort with an exception).

Swift generics not preserving type

This code works as expected.

class BaseClass {

required init() {} // <-- ADDED THIS

func printme() -> Void {
println("I am BaseClass")
}
}

class DerivedClass : BaseClass {
override func printme() -> Void {
println("I am DerivedClass")
}
}

class Util<T: BaseClass> {
func doSomething() {
var instance = T()
instance.printme()
}
}

var util = Util<DerivedClass>()
util.doSomething()

Code base are stolen from @GregoryHigley answer :)

Marking init() {} as required did the thing.
This guarantees init() is the designated initializer of ANY derived class from BaseClass.

Without it, one can make illegal subclass like:

class IllegalDerivedClass : BaseClass {
var name:String

init(name:String) {
self.name = name
super.init()
}

override func printme() -> Void {
println("I am DerivedClass")
}
}

var util = Util<IllegalDerivedClass>()
util.doSomething()

You know this doesn't work because IllegalDerivedClass doesn't inherit init() initializer.

I think, that is the reason of your problem.

Anyway, whose fault is that?

  • Compiler should warn about ambiguousness.
  • Runtime should try to initialize DerivedClass() as specified with T.
  • Debugger should show instance is a instance of BaseClass as it actually is.

ADDED:

As of Xcode 6.1 GM 2, It seems, you need more work. (in addition to required init() {})

class Util<T: BaseClass> {
let theClass = T.self // store type itself to variable

func doSomething() {
var instance = theClass() // then initialize
instance.printme()
}
}

I have absolutely no idea why we need this, what's going on X(

ADDED:2014/10/18

I found this also works:

    func doSomething() {
var instance = (T.self as T.Type)()
instance.printme()
}

ADDED: 2015/02/10

As of Xcode Version 6.3 (6D520o) / Swift 1.2

We no longer need (T.self as T.Type)() hack. Just T() works as long as T has required init() initializer.

class Util<T: BaseClass> {
func doSomething() {
var instance = T()
instance.printme()
}
}

Subclassing Objective-C class with factory method in Swift, the generics doesn't work

Your factory method implementation in Obj-C is wrong. It always creates an instance of Base. To fix it:

+ (instancetype)baseWithContent:(NSString *)content {
Base *base = [[self alloc] init]; //self instead of Base
base.content = content;
return base;
}

Basically, your factory method implementation doesn't match its return type, the result are type problems in Swift because Swift trusts type declarations.

Generic parameter 'T' could not be inferred after assignment

This may or may not be a compiler bug.

It is known that Swift does not try to infer the types of some closures, namely, multi-statement ones, as said in SR-1570:

This is correct behavior: Swift does not infer parameter or return types from the bodies of multi-statement closures.

However, your closure consists of only one statement, one declaration to be specific. It is possible, albeit weird, that they designed it so that Swift doesn't try to infer types if the closure contains one declaration as well. For example, this does not compile either

f { let x: Int = $0 } // nothing to do with "g"! The issue seems to be with declarations

If this were by-design, the rationale behind it might be because a single declaration in a closure doesn't make much sense anyway. Whatever is declared, won't be used.

But again, this is just speculation, and this could be a bug as well.

To fix it, simply make it a not-a-declaration:

f { _ = g($0) } // this, without the "let", is IMO the idiomatic way to ignore the function result

Or

f { g($0) } // g has @discardableResult anyway, so you don't even need the wildcard

Swift: EXC_BAD_ACCESS calling a method from a generic type that implements a protocol

It is a compiler bug, resulting in a stack overflow when accessing the localObjects property. From what I can gather, it is an issue with a recursive dependency between your types, and more specifically that localObjects is invoked via dynamic dispatch. If I remove the static var localObjects: ResourceFinderType { get } from Resource it works.

Alternatively, if you remove the Resource constraint from ResourceFinder, it will also run successfully.

I have reported the issue and you can track it via SR-1314



Related Topics



Leave a reply



Submit