当您使用泛型函数时,泛型参数的类型是从您将结果分配给的变量的类型推断出来的(或者,对于返回 Void 的函数,从您指定的参数的具体类型推断出来)传递给T 类型的参数)。
在您的示例中,您没有告诉编译器您的 vc 变量是什么类型,因此您会收到错误消息。您可以像这样轻松解决此问题:
class MyViewController: UIViewController, Routable {
public static func provideInstance(id: String?) -> Self? {
return self.init()
}
}
// give `vc` a concrete type so `T` can be inferred:
let vc: MyViewController? = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!)
编辑:
由于您的问题似乎更接近于“如何复制 Objective-C 的 UIViewController *<Routable> 概念”,而您在 Swift 中无法做到这一点,我会提到您可能会发现值得回顾你的设计。当我从 Objective-C 迁移到 Swift 时,我认为无法使用像 UIViewController *<Routable> 这样的东西很难解决(甚至向 Apple 提交了一个错误报告),但实际上这不是问题。
您的Router 类需要您的视图控制器中的一些 信息才能正确路由。在您的示例中,您正在寻找与特定 URL 关联的视图控制器。这意味着您的 UIViewController 有一个包含此 URL 的 property,因此与其返回 Routable 协议,正确的方法是像这样子类化 UIViewController:
class RoutableViewController: UIViewController {
let url: URL = URL(string: "...")
}
现在你的路由器看起来像:
class Router {
var routables = [RoutableViewController]()
func viewControllerWithURL(_ url: URL) -> RoutableViewController? {
return routables.first
}
}
现在您不需要进行任何类型转换,并且(显然)维护了类型安全。
编辑 2:
这是一个使 每个 视图控制器符合 Routable 的解决方案(与我在之前的编辑中提出的解决方案相比,我认为您不会获得太多收益,但无论如何都是这样):
protocol Routable {
var url: URL? { get }
}
// now every UIViewController conforms to Routable
extension UIViewController: Routable {
var url: URL? { return nil }
}
class MyViewController: UIViewController {
private let myURL: URL
override var url: URL? { return myURL }
init(url: URL) {
myURL = url
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class Router {
var viewControllers: [UIViewController] = [
MyViewController(url: URL(string: "foo")!),
UIViewController()
]
func viewController(for url: URL) -> UIViewController? {
for viewController in viewControllers {
if viewController.url == url { return viewController }
}
return nil
}
}