强制转换类型在泛型函数作用域中不起作用.为什么?



我在我的项目中做了一个类路由器(UINavigationController的子类(,用于集中和最小化用于实例化和在视图之间导航的代码。但是当我尝试最小化特定函数(buildView(时,类型转换不起作用。但这在函数buildView的范围之外工作得很好,与函数goHome中的as!运算符相同。

enum Routes {
case home
case account
var file: String {
switch self {
case .home:
return "HomeView"
case .account:
return "AccountView"
}
}
protocol HomeInterface: class {
func goTo(view: Routes)
func showModal(view: Routes, caller: UIViewController)
}
class HomePresenter: NSObject, HomeInterface {
init(view: HomeViewInterface) {
self.view = view
}
internal func goTo(view: Routes) { /* Implementation */ }
internal func showModal(view: Routes, caller: UIViewController) {/* Implementation */ }
}

protocol HomeViewInterface: class { 
/* Implementation */
}
class HomeViewController: UIViewController, HomeViewInterface {
var presenter: HomeInterface?
override func viewDidLoad() {
super.viewDidLoad()
}
/* Implementation */
}

工作代码

func goHome() {
let viewInstance = buildView(view.file, HomeViewController.identifier, HomeViewController.self)
viewInstance.presenter = HomePresenter(view: viewInstance)
self.view?.pushViewController(viewInstance, animated: true)
}
private func buildView<T>(_ nameFile: String, _ identifier: String, _ viewClass: T.Type) -> T {
return UIStoryboard(name: nameFile, bundle: nil).instantiateViewController(withIdentifier: identifier) as! T
}

所需的最终代码,但不起作用:

func goHome() { 
buildViewFinal(view.file, HomeViewController.identifier, HomeViewController.self)
}
func buildViewFinal<T, P>(_ nameFile: String, _ identifier: String, viewClass: T, presenter: P) {
let viewInstance = UIStoryboard(name: nameFile, bundle: nil).instantiateViewController(withIdentifier: identifier) as? T
viewInstance.presenter = P(view: viewInstance)
self.view?.pushViewController(viewInstance, animated: true)
}

当我尝试仅将代码最小化以buildViewFinal函数时,无法识别viewInstance的属性presenter,显示编译错误

类型为"T?"的值没有成员"演示者">

,并在pushViewController显示错误:

无法将类型"T?"的值转换为预期的元素类型 'UIViewController'

主要目标是将所有代码转换为创建和导航有用且简单。

那么,这在第一段代码中如何正常工作,但在识别作用域内的buildViewFinal类型时却失败了?

在第一段代码中,您将HomeViewController.self作为viewClass传递,因此它知道buildViewFinal将返回HomeViewController的实例,并且HomeViewController具有presenter属性。

在第二个代码片段中,编译器对T一无所知,因此它不能假设它将具有presenter属性。

可以使用类型约束来强制THomeViewController或任何类定义presenter属性:

func buildViewFinal<T: HomeInterface, P>(_ nameFile: String, _ identifier: String, viewClass: T, presenter: P) {
let viewInstance = UIStoryboard(name: nameFile, bundle: nil).instantiateViewController(withIdentifier: identifier) as? T
viewInstance.presenter = P(view: viewInstance)
self.view?.pushViewController(viewInstance, animated: true)
}

但是接下来你会遇到一个问题,viewInstance无法推送,因为编译器不知道它是UIViewController子类的实例。

真正的泛型和协议只是使这里的事情复杂化。

您正在处理面向类的UIKit,因此您不妨使用良好的旧继承

最新更新