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

Ste*_*ton 6 generics ios swift

我正在尝试编写一系列泛型函数,它们将通过传递UIViewController类或子类Type 对视图控制器堆栈进行排序,然后返回"找到"viewController或nil的实例.到目前为止,我甚至无法编译这个简单的代码片段:

extension UINavigationController {

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

并会打电话给:

navController.fhk_find(fooViewController.self)
Run Code Online (Sandbox Code Playgroud)

但是,编译器告诉我这viewControllerType不是一个类型.

我不确定我在这里缺少什么......

dfr*_*fri 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
    }
}
Run Code Online (Sandbox Code Playgroud)

用法示例如下。请注意,您呼叫fooViewController.dynamicType而不是fooViewController.self。后者为您提供而不是类型的fooViewController.self,即fooViewController.self = fooViewController.self

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


使用fhkFindFirst(...)识别子类的实例:OK

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

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> */
Run Code Online (Sandbox Code Playgroud)

使用fhkFindFirst(...)发现超实例:未如预期

而在以下情况下,fooViewControllerUIViewController对象,并且被错误地标识为BarViewController,因为BarViewController对象(in中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> */
Run Code Online (Sandbox Code Playgroud)

包起来 只要您仅使用该方法来搜索的子类,上述方法就会起作用UIViewController;相反,如果您尝试搜索超类对象,则会在中获得第一个控制器UINavigationController.viewControllers


关于edelaney05:s的相关问题,请参见以下评论

我将从引用问题开始,以防评论被删除:

edelaney05:有没有办法防止这种情况发生?这是一个非常有趣的边缘情况,特别是如果您有中级课程的话。class FooVC: AwesomeVC { ... }class BarVC: AwesomeVC { ... }class AwesomeVC: UIViewController { ... }

是的,如果您知道将使用的纯(第一级)子类之外的其他方法,则可以解决此问题UIViewController。例如,允许查找

  • 的子类UIViewController。(+)
  • 这些子类的子类。(++)

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

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

class FooBarViewController: UIViewController { }

class FooViewController : FooBarViewController { }
class BarViewController : FooBarViewController { }

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

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

print(navController.fhkFindFirst(FooViewController) ?? "None found.")
/* <__lldb_expr_1582.FooViewController: 0x7fe22a712e40> */

print(navController.fhkFindFirst(BarViewController) ?? "None found.")
/* <__lldb_expr_1582.BarViewController: 0x7fe22a4196a0> */

print(navController.fhkFindFirst(FooBarViewController) ?? "None found.")
/* <__lldb_expr_1582.FooBarViewController: 0x7fe22a70ee60> */
Run Code Online (Sandbox Code Playgroud)

  • @StephenNewton注意我的编辑:只要您仅搜索UIViewController的子类实例,上述解决方案就可以很好地工作,但是如果您尝试搜索超类对象(UIViewController,则总是在UINavigationController.viewControllers中返回第一个实例)。 )。不过,这应该没问题,因为您的所有视图控制器都应该是UIViewController子类的实例。 (2认同)