![](/img/trans.png)
[英]Passing parameter to method which implements protocol and extends a class in swift
[英]swift method parameter of type that implements a protocol?
试图在快速的UIViewController<Routable>
实现类似于以下目标C代码的功能,但是它抱怨无法推断出通用类型
我认为它希望我以某种方式定义要使用的UIViewController的子类,但是我不在乎。 我只想要一个实现Routable的UIViewController
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,但是我宁愿做对了
您期望vc
在这种情况下是哪种精确类型? (我的意思是类型可以在编译时确定,请看上面的代码,而不是“直到运行时才知道的某种类型。”)如果您无法回答,那么编译器也不能。
正确的方法是完全按照您所说的“返回UIViewController或Routable”。 这就是您所知道的一切。 试图说一些更精确的说法是不正确的。
vc
是类型推断的,这只是一个方便。 它在编译时仍必须具有特定的已知类型。 如果您无法在源代码中编写它,则编译器将无法推断它。
使用泛型函数时,泛型参数的类型是根据将结果分配给变量的类型来推断的(或者,对于返回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的属性 ,因此正确的方法是像这样子类化UIViewController,而不是返回Routable
协议:
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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.