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不是一个类型.
我不确定我在这里缺少什么......
您需要将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(...)发现超实例:未如预期
而在以下情况下,fooViewController是UIViewController对象,并且被错误地标识为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)