一尘不染

如果不应该,则可选绑定成功

swift

这是我在Swift(略作修改)中发布的遍历视图控制器层次结构的可能解决方案:

extension UIViewController {

    func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? {
        var currentVC = self
        while let parentVC = currentVC.parentViewController {
            println("comparing \(parentVC) to \(T.description())")
            if let result = parentVC as? T { // (XXX)
                return result
            }
            currentVC = parentVC
        }
        return nil
    }
}

该方法应遍历父视图控制器层次结构,并返回给定类的第一个实例;如果未找到,则返回nil。

但这是行不通的,我也不知道为什么。标有的可选绑定(XXX) 始终会成功执行 ,因此即使第一个父视图控制器不是的实例,也将返回它T

这很容易被复制:创建一个从在Xcode6通用的“iOS的主从应用程序”模板的项目,下面的代码添加到viewDidLoad()该的MasterViewController类:

if let vc = self.traverseAndFindClass(UICollectionViewController.self) {
    println("found: \(vc)")
} else {
    println("not found")
}

selfMasterViewController(的子类UITableViewController),并且其父视图控制器是UINavigationControllerUICollectionViewController父视图控制器层次结构中没有该类,因此我希望该方法返回nil并且输出“未找到”。

但这是发生了什么:

comparing <UINavigationController: 0x7fbc00c4de10> to UICollectionViewController
found: <UINavigationController: 0x7fbc00c4de10>

这显然是错误的,因为UINavigationController它不是的子类
UICollectionViewController。也许我犯了一些愚蠢的错误,但我找不到它。


为了找出问题,我还尝试使用独立于UIKit的自己的类层次结构来重现它:

class BaseClass : NSObject {
    var parentViewController : BaseClass?
}

class FirstSubClass : BaseClass { }

class SecondSubClass : BaseClass { }

extension BaseClass {

    func traverseAndFindClass<T where T : BaseClass>(T.Type) -> T? {
        var currentVC = self
        while let parentVC = currentVC.parentViewController {
            println("comparing \(parentVC) to \(T.description())")
            if let result = parentVC as? T { // (XXX)
                return result
            }
            currentVC = parentVC
        }
        return nil
    }
}

let base = BaseClass()
base.parentViewController = FirstSubClass()

if let result = base.traverseAndFindClass(SecondSubClass.self) {
    println("found: \(result)")
} else {
    println("not found")
}

你猜怎么着?现在可以正常工作了!输出是

comparing <MyApp.FirstSubClass: 0x7fff38f78c40> to MyApp.SecondSubClass
not found

更新:

  • 在通用方法中删除类型约束
    func traverseAndFindClass<T>(T.Type) -> T?
    

如@POB在评论中所建议,使其按预期工作。

  • 用“两步绑定”替换可选绑定
    if let result = parentVC as Any as? T { // (XXX)
    

正如@vacawama在他的回答中所建议的,它也可以按预期工作。

  • 将构建配置从“调试”更改为“发布”还可以使该方法按预期工作。(到目前为止,我仅在iOS模拟器中对此进行了测试。)

最后一点可能表明这是Swift编译器或运行时错误。而且我仍然无法理解为什么问题出现在的子类中UIViewController,而不是出现在我的子类中BaseClass。因此,在接受答案之前,我将让问题开放一段时间。


更新2:Xcode 7起, 此问题已修复。

在最终的Xcode 7版本中,该问题不再发生。现在,在Release和Debug配置中if let result = parentVC as? T,该traverseAndFindClass()方法中的可选绑定 均按预期工作(并且失败)。


阅读 243

收藏
2020-07-07

共1个答案

一尘不染

如果您尝试在Playground中将类型UINavigationController为object的对象UICollectionViewController强制转换为:

var nc = UINavigationController()

if let vc = nc as? UICollectionViewController {
    println("Yes")
} else {
    println("No")
}

您收到此错误:

游乐场执行失败:: 33:16:错误:如果让vc = nc为“ UICollectionViewController”不是“
UINavigationController”的子类型? UICollectionViewController {

但是,如果您这样做:

var nc = UINavigationController()

if let vc = (nc as Any) as? UICollectionViewController {
    println("Yes")
} else {
    println("No")
}

它显示“否”。

所以我建议尝试:

extension UIViewController {

    func traverseAndFindClass<T where T : UIViewController>(T.Type) -> T? {
        var currentVC = self
        while let parentVC = currentVC.parentViewController {
            println("comparing \(parentVC) to \(T.description())")
            if let result = (parentVC as Any) as? T { // (XXX)
                return result
            }
            currentVC = parentVC
        }
        return nil
    }
}
2020-07-07