尝试在swift中实现类似以下目标c代码的内容
public protocol Routable {
static func provideInstance(id: String?) -> Self?
}
public class Router {
public func getDestinationViewController<T: UIViewController>(url: URL) -> T? where T: Routable {
return nil
}
}
// Generic parameter `T` could not be inferred
let vc = Router().getDestinationViewController(url: URL(string: "www.domain.com/users/1")!)
但它抱怨它无法推断泛型类型
我认为它要我以某种方式定义要使用的UIViewController的子类,但我不在乎。我只想要一个实现Routable
的UIViewControllervertSizer
我知道我可以使用强制转换解决问题,只是让方法返回UIViewController或Routable,但我宁愿以正确的方式执行
答案 0 :(得分:3)
在这种情况下,您期望vc
的确切类型是什么? (我的意思是在编译时可以确定类型,查看上面的代码,而不是“直到运行时我们不知道的某种类型。”)如果你不能回答,那么编译器也不能。
正确的方法是“返回UIViewController或Routable”,就像你说的那样。这就是你现在知道的一切。试图说出更精确的内容是不正确的。
vc
类型推断的事实只是一种便利。它仍然必须在编译时具有特定的已知类型。如果你不能在源代码中编写它,编译器就无法推断它。
答案 1 :(得分:1)
当您使用泛型函数时,泛型参数的类型是从您为其分配结果的变量的类型推断出来的(或者,对于返回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")!)
编辑:
因为你的问题似乎更像是&#34;我如何复制Objective-C的UIViewController *<Routable>
&#34;的概念,你无法做到这一点斯威夫特,我提到你可能会觉得值得回顾一下你的设计。当我从Objective-C转移到Swift时,我认为不能使用类似UIViewController *<Routable>
之类的东西很难绕过(甚至向Apple提交了关于它的错误报告),但实际上它还没有是一个问题。
您的Router
课程需要在视图控制器中部分信息才能正确路由。在您的示例中,您正在查找与特定URL关联的视图控制器。这意味着您的UIViewController
具有包含此URL的属性,因此不是返回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
}
}