一尘不染

声明符合协议的类的数组

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)
    }
}

现在,我想声明一个数组,其中包含符合此类协议的对象类。我该如何申报?理想情况下,我希望编译器检查数组(在编译时)是否正确填充,而不是自己在运行时(运行时)检查它as

这是我尝试的没有成功的方法:(

  1. 这会导致编译错误:

‘任何对象都没有名为’construct’的成员

    var array = [
    MyConstructor.self,
    MyOtherConstructor.self,
]

var controller = array[0].construct() // << ERROR here
  1. 由于类本身不符合协议(它们的实例符合),因此编写这种方法甚至更糟。

类型“ MyConstructor.Type”不符合协议“ ControllerConstructorProtocol”

    var array: Array<ControllerConstructorProtocol> = [
    MyConstructor.self, // << ERROR here
    MyOtherConstructor.self,
]

编辑2016/04/23 :在Swift 2.2(Xcode7.3)中,可以写@rintaro的原始想法:)

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

阅读 200

收藏
2020-07-07

共1个答案

一尘不染

可以像这样声明“符合协议的类的数组” Array<TheProtocol.Type>

您可以:

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

但…,

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

该项目的调用方法是“未实现”。

到目前为止,您必须将协议声明为@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()

注意:投放问题已在Swift 1.2 / Xcode 6.3中修复。但是“未实现”是“未实现” :(


只是随机的想法:

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

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

let vc = array[0]()

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

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()
2020-07-07