可选绑定成功,如果它不应该 [英] Optional binding succeeds if it shouldn't

查看:134
本文介绍了可选绑定成功,如果它不应该的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' UIViewController>(T.Type) - > T' {
var currentVC = self
while let parentVC = currentVC.parentViewController {
println(比较\(parentVC)到\(T.description()))
如果让结果= parentVC为? T {//(XXX)
返回结果
}
currentVC = parentVC
}
返回零
}
}

该方法应该遍历父视图控制器层次结构并返回给定类的第一个
实例,或者如果没有找到零。



但它不起作用,我不知道为什么。用(XXX) 标记的可选绑定
始终会成功
,以便第一个父视图控制器返回
,即使它不是 T 的实例。



这可以很容易地复制:从iOS Master - 详细应用程序
模板,将以下代码添加到
MasterViewController viewDidLoad() / code> class:

 如果让vc = self.traverseAndFindClass(UICollectionViewController.self){
println (found:\(vc))
} else {
println(not found)
}

self 是 MasterViewController (<$ c的子类$ c> UITableViewController ),它的
父视图控制器是一个 UINavigationController
父视图
控制器层次结构中没有 UICollectionViewController ,所以我期望方法
返回 nil ,输出为not found。



但是,这会发生什么情况:

 比较< UINavigationController:0x7fbc00c4de10>到UICollectionViewController 
找到:< UINavigationController:0x7fbc00c4de10>

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






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

  class BaseClass:NSObject {
var parentViewController:BaseClass?
}

class FirstSubClass:BaseClass {}
$ b $ class SecondSubClass:BaseClass {}

extension BaseClass {

func traverseAndFindClass< T where T:BaseClass>(T.Type) - > T' {
var currentVC = self
while let parentVC = currentVC.parentViewController {
println(比较\(parentVC)到\(T.description()))
如果让结果= parentVC为? T {//(XXX)
返回结果
}
currentVC = parentVC
}
返回零
}
}

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

如果让result = base.traverseAndFindClass(SecondSubClass.self){
println(found :\(result))
} else {
println(not found)
}

猜猜是什么?现在它按预期工作!输出结果是:

 比较< MyApp.FirstSubClass:0x7fff38f78c40>到MyApp.SecondSubClass 
未找到






更新:




  • 删除泛型方法中的类型约束

      func traverseAndFindClass< T>(T.Type) - > T' 

    正如@POB在评论中所建议的那样,它可以按预期工作。


  • 通过两步绑定替换可选绑定

      if let result = parentVC as Any as?在他的回答中,@vacawama建议的T {//(XXX)

    也可以按预期工作。

  • 将构建配置从调试更改为发布也会使
    方法按预期工作。 (我已经在iOS模拟器中测试过)。

    $ b

    最后一点可能表明这是一个Swift编译器或运行时错误。我仍然
    不知道为什么问题发生在 UIViewController 的子类中,但是没有
    和我的 BaseClass 子类。 code>。因此,在接受答案之前,我会保持问题的答案为





    更新2 : Xcode 7 已被修正。



    在最终的Xcode 7
    版本中,再也发生了。可选绑定
    if let result = parentVC as? traverseAndFindClass()方法
    中的T
    现在可以按照预期工作(并且失败),无论是在Release还是Debug配置中。

    解决方案

    如果您尝试有条件地将 UINavigationController 类型的对象转换为 UICollectionViewController 在Playground中:

      var nc = UINavigationController()

    如果让vc = nc as? UICollectionViewController {
    println(Yes)
    } else {
    println(No)
    }

    你得到这个错误:

    Playground执行失败:33:16:error:'UICollectionViewController'不是'UINavigationController'
    的子类型,如果让vc = nc as? UICollectionViewController {



    但是如果您改为:

      var nc = UINavigationController()

    如果让vc =(nc as Any)as? UICollectionViewController {
    println(Yes)
    } else {
    println(No)
    }

    打印出No。

    所以我建议您尝试:

     扩展UIViewController {

    func traverseAndFindClass< T where T:UIViewController>(T.Type) - > T' {
    var currentVC = self
    while let parentVC = currentVC.parentViewController {
    println(比较\(parentVC)到\(T.description()))
    如果让结果=(parentVC as Any)as? T {//(XXX)
    返回结果
    }
    currentVC = parentVC
    }
    返回零
    }
    }


    This is what I posted as a possible solution to Traverse view controller hierarchy in Swift (slightly modified):

    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
        }
    }
    

    The method should traverse up the parent view controller hierarchy and return the first instance of the given class, or nil if none is found.

    But it does not work, and I cannot figure out why. The optional binding marked with (XXX) always succeeds, so that the first parent view controller is returned even if it is not an instance of T.

    This can easily be reproduced: Create a project from the "iOS Master-Detail Application" template in Xcode 6 GM, and add the following code to viewDidLoad() of the MasterViewController class:

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

    self is a MasterViewController (a subclass of UITableViewController), and its parent view controller is a UINavigationController. There is no UICollectionViewController in the parent view controllers hierarchy, so I would expect that the method returns nil and the output is "not found".

    But this is what happens:

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

    This is obviously wrong, because UINavigationController is not a subclass of UICollectionViewController. Perhaps I made some stupid error, but I could not find it.


    In order to isolate the problem, I also tried to reproduce it with my own class hierarchy, independent of 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")
    }
    

    And guess what? Now it works as expected! The output is

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


    UPDATE:

    • Removing the type constraint in the generic method

      func traverseAndFindClass<T>(T.Type) -> T?
      

      as suggested by @POB in a comment makes it work as expected.

    • Replacing the optional binding by a "two-step binding"

      if let result = parentVC as Any as? T { // (XXX)
      

      as suggested by @vacawama in his answer also makes it work as expected.

    • Changing the build configuration from "Debug" to "Release" also makes the method work as expected. (I have tested this only in the iOS Simulator so far.)

    The last point could indicate that this is a Swift compiler or runtime bug. And I still cannot see why the problem occurs with subclasses of UIViewController, but not with subclasses of my BaseClass. Therefore I will keep the question open for a while before accepting an answer.


    UPDATE 2: This has been fixed as of Xcode 7.

    With the final Xcode 7 release the problem does not occur anymore. The optional binding if let result = parentVC as? T in the traverseAndFindClass() method now works (and fails) as expected, both in Release and Debug configuration.

    解决方案

    If you try to conditionally cast an object of type UINavigationController to a UICollectionViewController in a Playground:

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

    You get this error:

    Playground execution failed: :33:16: error: 'UICollectionViewController' is not a subtype of 'UINavigationController' if let vc = nc as? UICollectionViewController {

    but if instead you do:

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

    it prints "No".

    So I suggest trying:

    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
        }
    }
    

    这篇关于可选绑定成功,如果它不应该的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆