实现协议的类型的swift方法参数?

时间:2016-11-30 17:12:02

标签: swift generics protocols

尝试在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

的UIViewController
vertSizer

我知道我可以使用强制转换解决问题,只是让方法返回UIViewController或Routable,但我宁愿以正确的方式执行

2 个答案:

答案 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
    }
}