声明符合协议的类数组

nac*_*o4d 8 swift

让我们说我已经创建了这个协议和几个类

import UIKit

protocol ControllerConstructorProtocol {
    class func construct() -> UIViewController?
}

class MyConstructor: ControllerConstructorProtocol {
    class func construct() -> UIViewController? {
        return UIViewController()
    }
}

class MyOtherConstructor: ControllerConstructorProtocol {
    class func construct() -> UIViewController? {
        return UITableViewController(style: .Grouped)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我想声明一个包含符合这种协议的对象类的数组.我怎么声明呢?理想情况下,我希望编译器检查数组是否正确填充(在编译时),而不是在运行时自己检查(运行时)as.

这是我尝试过没有成功:(

  1. 这会导致编译错误:

    '任何对象没有名为'construct'的成员

    var array = [
        MyConstructor.self,
        MyOtherConstructor.self,
    ]
    
    var controller = array[0].construct() // << ERROR here
    
    Run Code Online (Sandbox Code Playgroud)
  2. 写这个更糟糕,因为类本身不符合协议(他们的实例)

    类型'MyConstructor.Type'不符合协议'ControllerConstructorProtocol'

    var array: Array<ControllerConstructorProtocol> = [
        MyConstructor.self, // << ERROR here
        MyOtherConstructor.self,
    ]
    
    Run Code Online (Sandbox Code Playgroud)

编辑2016/04/23:在Swift 2.2(Xcode 7.3)中,可以编写@ rintaro的原创想法 :)

let array: Array<ControllerConstructorProtocol.Type> = [
    MyConstructor.self,
    MyOtherConstructor.self,
]
let viewController = array[0].construct()
Run Code Online (Sandbox Code Playgroud)

rin*_*aro 10

"符合协议的类数组"可以声明为Array<TheProtocol.Type>.

您可以:

var array: Array<ControllerConstructorProtocol.Type> = [
    MyConstructor.self,
    MyOtherConstructor.self,
]
Run Code Online (Sandbox Code Playgroud)

但...,

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

该项目的调用方法是"未实现".

截至目前,您必须将协议声明为@objc,并通过调用方法AnyClass.而且,由于某些原因,我们不能直接array[0]转向AnyClass,而是我们必须将其投射到Any那时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()
Run Code Online (Sandbox Code Playgroud)

注意:在Swift 1.2/Xcode 6.3中修复了转换问题.但"未实现"是"未实现的":(


随意的想法:

这取决于您的实际用例,但在这种特殊情况下,()-> UIViewController?闭包数组就足够了:

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

let vc = array[0]()
Run Code Online (Sandbox Code Playgroud)

如果您有多种方法,则可能需要使用协议的类型擦除包装器.

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()
Run Code Online (Sandbox Code Playgroud)