【问题标题】:Swift: AnyObject as? Array casting fail if the array is empty (not nil)斯威夫特:AnyObject 作为?如果数组为空(非零),则数组转换失败
【发布时间】:2017-10-23 01:43:47
【问题描述】:

情况就是这样:假设我得到一个变量作为 AnyObject 或 Any。然后,我必须强制转换变量以了解它是否是包含特定类型对象的数组。

func myFuction(receivedObject: AnyObject) {
    if let validDogs = receivedObject as? [Dog] {

        print("Received object is an array of dogs")
        // Do something with valid dogs
    }

    if let validCats = receivedObject as? [Cat] {

        print("Received object is an array of cats")
        // Do something with valid cats
    }
}

如果接收到的对象不是空数组(不是 nil),则此代码有效,但如果接收到的对象是空数组,则失败,因为我的日志打印了这两条消息:

"Received object is an array of dogs"
"Received object is an array of cats"

这表明对于空数组,强制转换失败。那么,有没有办法解决这个问题?

【问题讨论】:

  • 我看不出如何将任何东西的数组作为AnyObject 传递,因为数组是一个结构,而不是一个类。当然,除非它是NSArray。告诉我们你怎么称呼它。
  • 嗨@Grimxn。见:self.myFucntion(receivedObject: object as! AnyObject)
  • 好的,但是如果你不强制它,你就不能传入,比如[Cat()]——它不会编译。
  • 嗯,这里正在编译。我正在使用 Xcode 8.3.3
  • 要清楚,我的意思是你不能打电话给myFuction(receivedObject: [Cat()])——至少在Xcode 9.1中不能......你可以打电话给myFuction(receivedObject: [Cat()] as! AnyObject)

标签: arrays swift casting


【解决方案1】:

这样的代码强烈表明您的类型设计中存在严重问题,应该通过删除AnyObject 来解决。传递AnyObject 是正确的工具是非常罕见的。

你在这里说了一些很重要的话:

如果接收到的对象不是空数组(不是 nil),则此代码有效

空数组与nil 不同。如果你通过nil,那是一个可选的。 Optional<[Cat]>[Cat] 不一样,你不应该期望它始终如一地投射 as?,特别是如果它是 nil。如果这来自 ObjC,那么 nil 将桥接到实际的 Obj-C nil(这只是值 0),并且运行时实际上没有任何东西可以使用。

不过,您说您收到了两条日志行。这表明 as? 的两个演员都是成功的,而不是失败的。如果是这种情况,我假设这是一个NSArray,而一个空的NSArray 可以合法地将as? 强制转换为任何数组(NSArray 内部没有元素类型)。所以上面是预期的。

如果它真的是可选的,那么对于“我如何确定它是可选的,然后打开它然后确定它是否是[Cat]”的问题,答案是“停止;你也走了与AnyObject 相去甚远。”首先重新设计这个,这样你就不需要那个了,这通常意味着更早地弄清楚你的类型。

做你想做的事情的正确方法通常是使用重载,而不是as? 强制转换:

func myFuction(receivedObject: [Dog]) {
    print("Received object is an array of dogs")
    // Do something with valid dogs
}

func myFuction(receivedObject: [Cat]) {
    print("Received object is an array of cats")
    // Do something with valid cats
}

您不能只将AnyObject 传递给上述函数。你需要知道你正在使用的事物的类型。 (您使用as! AnyObject 剥离类型的评论表明您确实已经知道这些类型,并且正在积极地将它们丢弃并尝试稍后恢复它们。如果是这种情况,上面的代码是完全正确的。不要扔掉类型。)

虽然在某些极端情况下您无法知道这些类型(因为您实际上是在接受“任何对象”),但在绝大多数情况下,不知道您的类型表明存在设计问题。

【讨论】:

    【解决方案2】:

    从良好的编程角度来看,@RobNapier 的回答是正确的 - 我认为没有任何理由去做你正在尝试的事情,但它显示了一些有趣的东西。

    首先,您不能使用包含任何内容的数组来调用您的函数 - 数组,如 structs,不符合 AnyObject(类这样做)。但是,您通过强制转换来调用该函数 - 因为任何东西都可以转换为 AnyObject(可能是通过将结构包装在退化类中),所以调用会编译。

    其次,你的问题有错误的推论 - 你说“这表明对于空数组,强制转换失败。” - 不是这样,演员成功两种情况...

    让我们看看实际传入的是什么:

    class Pet { var name: String { return "" } }
    class Dog: Pet { override var name: String { return "Fido" } }
    class Cat: Pet { override var name: String { return "Cfor" } }
    
    func myFuction(receivedObject: AnyObject) {
        print("myFuction called with \(receivedObject)") // ***
        if let validDogs = receivedObject as? [Dog] {
    
            print("Received object is an array of dogs")
            // Do something with valid dogs
        }
    
        if let validCats = receivedObject as? [Cat] {
    
            print("Received object is an array of cats")
            // Do something with valid cats
        }
    }
    
    var a: [Cat] = [Cat()]
    //myFuction(receivedObject: a) // Argument type '[Cat]' does not conform to expected type 'AnyObject'
    myFuction(receivedObject: a as! AnyObject) // Forced cast from '[Cat]' to 'AnyObject' always succeeds; did you mean to use 'as'?
    a = []
    myFuction(receivedObject: a as! AnyObject) // Forced cast from '[Cat]' to 'AnyObject' always succeeds; did you mean to use 'as'?
    

    输出:

    myFuction called with (
        "__lldb_expr_97.Cat"
    )
    Received object is an array of cats
    myFuction called with (
    )
    Received object is an array of dogs
    Received object is an array of cats
    

    因此,当使用非空数组调用它时,类型是从成员中推断出来的,并且只有当成员的类型正确时,转换才会成功。空数组没有成员,因此也可以是 [Cat][Dog] 或实际上是 [String]

    试试

    let b = [Cat(), Dog()]
    myFuction(receivedObject: b as! AnyObject)
    

    打印出来

    myFuction called with (
        "__lldb_expr_107.Cat",
        "__lldb_expr_107.Dog"
    )
    

    (并且不打印强制转换“成功”)

    它的长和短是

    A) as! AnyObject 的作用力有效地丢弃了 array 类型并传递了类似于元素元组的东西,let _ = as? 能够将这些元素的类型重新组合成一个数组。

    B) 重新阅读@RobNapier 的答案 - 这就是要走的路!

    【讨论】:

      猜你喜欢
      • 2016-06-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-07
      相关资源
      最近更新 更多