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

时间:2014-09-14 21:07:18

标签: ios generics swift

这是我发布的Traverse view controller hierarchy in 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的实例。

可以轻松复制:从“iOS Master-Detail Application”创建项目 Xcode 6 GM中的模板,并将以下代码添加到viewDidLoad() MasterViewController课程:

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

selfMasterViewControllerUITableViewController的子类)及其 父视图控制器是UINavigationController。 父视图中没有UICollectionViewController 控制器层次结构,所以我期望这个方法 返回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在他的回答中提出的建议也使它按预期工作。

  • 将构建配置从“Debug”更改为“Release”也会使 方法按预期工作。 (到目前为止,我只在iOS模拟器中对此进行了测试。)

最后一点可能表明这是一个Swift编译器或运行时错误。我还是 无法理解为什么问题出现在UIViewController的子类中,但不是 使用我的BaseClass的子类。因此,我将把问题保持开放 在接受答案之前。


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

使用最终的Xcode 7 释放问题不再发生。可选绑定 if let result = parentVC as? T方法中的traverseAndFindClass() 现在可以在Release和Debug配置中按预期工作(和失败)。

1 个答案:

答案 0 :(得分:3)

如果您尝试将类型为UINavigationController的对象有条件地投射到游乐场中的UICollectionViewController

var nc = UINavigationController()

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

您收到此错误:

Playground执行失败:: 33:16:错误:'UICollectionViewController'不是'UINavigationController'的子类型 如果让vc = nc为? 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
    }
}