Swift Generics:使用T.Type作为参数的函数返回可选的T.

时间:2016-01-29 16:22:41

标签: ios swift generics

我尝试编写一系列通用函数,它们通过传递UIViewController类或子类Type来对一堆viewControllers进行排序,然后返回"找到的实例&# 34; viewController或者没有。到目前为止,我甚至无法编写这个简单的代码片段:

extension UINavigationController {

    func fhk_find<T: UIViewController>(viewControllerType: T.Type) -> T?
    {
        if let viewController = viewControllers.first as? viewControllerType {
            return viewController
        }
        else {
            return nil
        }
    }
}

并打电话:

navController.fhk_find(fooViewController.self)

但是,编译器告诉我viewControllerType不是类型。

我不确定我在这里失踪的是什么......

1 个答案:

答案 0 :(得分:5)

您需要将viewControllers.first(如果存在)转换为T,而不是参数viewControllerType。事实上,你根本不需要使用这个参数;您可以将扩展名修改为以下内容:

extension UINavigationController {
    func fhkFindFirst<T: UIViewController>(_: T.Type) -> T? {
        for viewController in viewControllers {
            if let viewController = viewController as? T {
                return viewController
            }
        }
        return nil
    }
}

示例用法如下。请注意,您使用fooViewController.dynamicType而非fooViewController.self进行了通话。后者为您提供fooViewController.self而不是类型,即fooViewController.self = fooViewController.self

现在请注意,尝试从子类类型到其超类的转换将始终成功,因此上面的解决方案将正确识别子类实例(UIViewController的子类),而如果查询超类实例(即,T作为超类UIViewController),即使viewController = viewController as? T实际上是viewController的子类的实例,UIViewController也会成功。

使用fhkFindFirst(...)标识子类实例:确定

在以下示例中,正确标识了两个子类实例,并将其引用返回给调用者。

class FooViewController : UIViewController { }
class BarViewController : UIViewController { }

let fooViewController = FooViewController()
let barViewController = BarViewController()

let navController = UINavigationController(rootViewController: fooViewController)
navController.addChildViewController(barViewController)

print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.")
// or: print(navController.fhkFindFirst(FooViewController))
/* <__lldb_expr_1582.FooViewController: 0x7fb97840ad80> */

print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.")
// or: print(navController.fhkFindFirst(BarViewController))
/* <__lldb_expr_1582.BarViewController: 0x7fb978709340> */

使用fhkFindFirst(...)查找超类实例:不按预期

在以下情况中,fooViewController是一个UIViewController对象,并且被错误标识为BarViewController,因为BarViewController对象的类型转换(在{ {1}})UINavigationController.viewControllers成功。

UIViewController

总结;只要您只使用该方法搜索class BarViewController : UIViewController { } let fooViewController = UIViewController() let barViewController = BarViewController() let navController = UINavigationController(rootViewController: barViewController) navController.addChildViewController(fooViewController) print(navController.fhkFindFirst(fooViewController.dynamicType) ?? "None found.") // or: print(navController.fhkFindFirst(UIViewController)) /* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> <-- "wrong" one */ print(navController.fhkFindFirst(barViewController.dynamicType) ?? "None found.") // or: print(navController.fhkFindFirst(BarViewController)) /* <__lldb_expr_1533.BarViewController: 0x7fa519e2bd40> */ 的子类,上述操作就会起作用;而如果你试图搜索一个超类对象,你将获得UIViewController中的第一个控制器。

关于edelaney05:以下评论中的相关问题的补充

我首先引用问题,以防删除评论:

  

edelaney05:有没有办法防范这个?这是一个非常有趣的边缘案例,特别是如果你有一个   中级班。 UINavigationController.viewControllersclass FooVC: AwesomeVC { ... }class BarVC: AwesomeVC { ... }

,您可以解决此问题,以防您知道自己将使用class AwesomeVC: UIViewController { ... }的纯(第一级)子类以外的其他工作;比如,允许找到

的第一个实例
  • UIViewController的子类。 (+)
  • 这些子类的子类。 (++)

我们可以利用动态类型比较来确保我们不执行子类实例到其超类的转换(因此错误地将子类实例标识为我们正在寻找的超类实例)。

UIViewController