繁体   English   中英

实现协议的类型的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM