2015-04-07 3 views
2

Исследование Swift generics и спотыкание о каком-то действительно странном поведении ... я должен подать радар, или я что-то не понимаю? Протестировано с бета-версией Swift 1.2.Swift Generic Factory: ошибка?

Кодекс говорит лучшее, простая реализация, которая сдерживает завод в инстанцировании BaseClass и производные классы:

/// Testing factory pattern to instantiate BaseClass and derived 
class BaseClassFactory 
{ 
    // Always returns an instance of BaseClass, even if the constant or 
    // variable storing the result is explicitly typed(see test) 
    class func makeInstance< T : BaseClass >(type: AnyClass) -> T? 
    { 
     return T() 
    } 

    // Returns an instance of DerivedClass only if the constant or variable storing 
    // the result is explicitly typed. 
    class func makeInstanceContrived< T : BaseClass >(type: AnyClass) -> T? 
    { 
     let instance : T? = T.makeInstance() as? T 
     return instance 
    } 

    // Works fine 
    class func makeInstanceAndCallBack< T : BaseClass >(handler: (instance: T?) -> Void) -> Void 
    { 
     let myInstance : T? = T.makeInstance() as? T 

     handler(instance: myInstance) 
    } 
} 

class BaseClass 
{ 
    // Since T() fails... 
    class func makeInstance()->BaseClass 
    { 
     return BaseClass() 
    } 
} 

class DerivedClass : BaseClass 
{ 
    override class func makeInstance()->BaseClass 
    { 
     return DerivedClass() 
    } 
} 

тесты, с скриншотом очень странное поведение (несмотря на предупреждение компилятора, то «есть» тест не проходит) : Compiler warning, and yet the is test passes!!!

// Nope 
if let instance = BaseClassFactory.makeInstance(DerivedClass.Type) 
{ 
    if instance is DerivedClass == false 
    { 
     println("1: Wrong type...") 
    } 
} 

// Nope, even when typing the constant. This seems like very dangerous behaviour... 
if let instance : DerivedClass = BaseClassFactory.makeInstance(DerivedClass.Type) 
{ 
    if instance is DerivedClass == false 
    { 
     //compiler even gives a warning here: " 'is' test is always true " 
     println("2: what the???") 
    } 
} 

// Nope 
if let contrivedInstance = BaseClassFactory.makeInstanceContrived(DerivedClass.Type) 
{ 
    if contrivedInstance is DerivedClass == false 
    { 
     println("3: Wrong type...") 
    } 
} 

// Yes, typing the constant does the trick here 
if let contrivedInstance : DerivedClass = BaseClassFactory.makeInstanceContrived(DerivedClass.Type) 
{ 
    println("4: success! type is: \(contrivedInstance)") 
} 

// Yes 
BaseClassFactory.makeInstanceAndCallBack() 
{ 
    (instance: DerivedClass?) -> Void in 

    if let callbackInstance = instance 
    { 
     println("5: success! type is: \(callbackInstance)") 
    } 
} 

ответ

3

у вас есть две проблемы здесь - один неверный код, другая известная ошибка (мой радар его был закрыт как DUP из 18518629, который по-прежнему открыт от 1.2b4).

Во-первых, в этой конструкции:

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

// then later 

BaseClassFactory.makeInstance(DerivedClass.Type) 

ваш аргумент ничего не делает. Это по сути бессмысленно и не способствует типу T (как он мог? Аргумент не ссылается T). Вместо этого тип T будет выбран из контекста, то есть если вы присвоите результат переменной DerivedClass, T будет DerivedClass. Если вы не укажете, поведение по умолчанию состоит в том, чтобы сделать T базовым классом, к которому он привязан, например, к BaseClass.

То, что вы, вероятно, означает это:

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

// then later 

BaseClassFactory.makeInstance(DerivedClass.self) 

Это должно сделать трюк установления T быть тип вы хотите. Кроме того, что это все еще не сработает, из-за ошибки, вызванной базовым классом, не имеющим динамически-отправленного инициализатора (существует только одна реализация среды выполнения, и она полагается на правильную инициализацию, называемую полиморфно).

Если вы добавите required init() { } в BaseType, вы получите правильное поведение.

+0

Большое спасибо. Я начал с T.Type в качестве аргумента, за исключением того, что он не работал без требуемого init ... Так как тест 4 прошел, я предположил, что компилятор смог вывести тип в зависимости от AnyClass.Type, который он вроде как кажется (с некоторая помощь). – Gregzo