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

这是我发布的 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")
}

self是一个MasterViewController(UITableViewController的子类),以及它
父视图控制器是一个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
释放问题不再发生.可选的绑定
如果让result = parentVC为? T在traverseAndFindClass()方法
现在可以在Release和Debug配置中按预期工作(并失败).

解决方法

如果您尝试有条件地将类型为UINavigationController的对象强制转换为Playground中的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
    }
}

相关文章

当我们远离最新的 iOS 16 更新版本时,我们听到了困扰 Apple...
欧版/美版 特别说一下,美版选错了 可能会永久丧失4G,不过只...
一般在接外包的时候, 通常第三方需要安装你的app进行测...
前言为了让更多的人永远记住12月13日,各大厂都在这一天将应...