Declare array of classes that conform to a protocol

后端 未结 4 1625
不知归路
不知归路 2020-12-19 04:40

Lets say I have created this protocol and a couple of classes

import UIKit

protocol ControllerConstructorProtocol {
    class func construct() -> UIViewC         


        
相关标签:
4条回答
  • 2020-12-19 05:16

    "array of classes that conform to a protocol" can be declared like Array<TheProtocol.Type>.

    You can:

    var array: Array<ControllerConstructorProtocol.Type> = [
        MyConstructor.self,
        MyOtherConstructor.self,
    ]
    

    But...,

        array[0].construct()
    //  ^ error: accessing members of protocol type value 'ControllerConstructorProtocol.Type' is unimplemented
    

    Calling method on the item is "unimplemented".

    As of now, you have to declare the protocol as @objc, and call the method via AnyClass. Moreover, for some reasons, we cannot directly cast array[0] to AnyClass, instead, we have to cast it to Any, then AnyClass.

    @objc protocol ControllerConstructorProtocol {
        class func construct() -> UIViewController?
    }
    
    var array: Array<ControllerConstructorProtocol.Type> = [
        MyConstructor.self,
        MyOtherConstructor.self,
    ]
    
    let vc = (array[0] as Any as AnyClass).construct()
    

    Note: Casting problem was fixed in Swift 1.2 / Xcode 6.3. But "unimplemented" is "unimplmented" :(


    Just random ideas:

    It's depends on your actual use-case, but in this particular case, array of ()-> UIViewController? closures is sufficient:

    var array: [() -> UIViewController?] = [
        MyConstructor.construct,
        MyOtherConstructor.construct,
    ]
    
    let vc = array[0]()
    

    If you have several methods, you might want to use type-erased wrapper of the protocol.

    protocol ControllerConstructorProtocol {
        class func construct() -> UIViewController?
        class func whoami() -> String
    }
    
    struct ControllerConstructorWrapper {
        private let _construct: () -> UIViewController?
        private let _whoami: () -> String
        init<T: ControllerConstructorProtocol>(_ t:T.Type) {
            _construct = { t.construct() }
            _whoami = { t.whoami() }
        }
        func construct() -> UIViewController? { return _construct() }
        func whoami() -> String { return _whoami() }
    }
    
    var array: [ControllerConstructorWrapper] = [
        ControllerConstructorWrapper(MyConstructor),
        ControllerConstructorWrapper(MyOtherConstructor),
    ]
    
    let who = array[0].whoami()
    let vc = array[0].construct()
    
    0 讨论(0)
  • 2020-12-19 05:17

    If you really want to sotre classes only (that conforms to protocol) then you can do it the following way (in Swift 3):

    If you want to create a new instance with the protocol type you have to add init() to protocol declaration:

    protocol SomeProtocol: ConformsToOtherProtocolIfNeeded {
        init(...) { ... }
        func someFunction(...) { ... }
    }
    
    class Class1: SomeProtocol {
        init(...) { ... }
        func someFunction(...) { ... }
    }
    
    class Class2: SomeProtocol {
        init(...) { ... }
        func someFunction(...) { ... }
    }
    

    declare array (as above):

    var someProtocols: Array<SomeProtocol.Type> = [
        Class1.self,
        Class2.self,
    ]
    

    and if you want to use someFunction, you have to create an instance, because the elements in the array not an instance. Example:

    for sp in someProtocols {
        let instance = sp.init()
        instance.someFunction()
    }
    

    If you want to compare the type of class, you also have to create an instance. (So you cannot use item of array directly (sp).)

    Examples:

    if type(of: instance) == type(of: otherInstance) { ... }
    if instance is SomeProtocol { ... }
    if instance is Class1 { ... }
    
    0 讨论(0)
  • 2020-12-19 05:22

    Not sure if I got your question completely, but why not this way:

    var array: [ControllerConstructorProtocol] = [MyConstructor(), MyOtherConstructor()]
    
    0 讨论(0)
  • 2020-12-19 05:25

    Try this:

    var myConst = MyConstructor()
    var myOthConst = MyOtherConstructor()
    
    var array:[AnyObject] = [
        myConst,
        myOthConst
    ]
    
    for a in array
    {
        if a is MyConstructor
        {
            println("a is type of MyConstructor")
            (a as! MyConstructor).myMethod()
        }
        else if a is MyOtherConstructor
        {
            println("a is type of MyOtherConstructor")
            (a as! MyOtherConstructor).myMethod()
        }
    }
    

    Another solution, although not that pretty...

    0 讨论(0)
提交回复
热议问题