Swift:AnyObject为?如果数组为空(不是nil),则数组转换失败

时间:2017-10-22 16:23:22

标签: arrays swift casting

情况就是这样:假设我得到一个变量为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"

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

2 个答案:

答案 0 :(得分:4)

这样的代码强烈暗示了你的类型设计中的一个深层次问题,应该通过删除AnyObject来解决。传递AnyObject是正确的工具非常罕见。

你在这里说了一些非常重要的事情:

  

如果接收的对象不是空数组(不是空)

,则此代码有效

空数组与nil不同。如果您通过nil,则该选项是可选的。 Optional<[Cat]>[Cat]不同,您不应期望它始终as?投射,特别是nil nil。如果这来自ObjC,那么nil将桥接到一个实际的Obj-C as?(它只是值0),并且运行时几乎无法使用。

但是你说你收到两个日志行。这表明两个NSArray演员阵容都在成功,而不是失败。如果是这种情况,我认为这是NSArray,空的as?可以合法地NSArray投射到任何数组([Cat]没有元素类型内部)。所以上述情况是预期的。

如果它确实是可选的,那么问题是&#34;如何确定它是可选的然后打开它然后解决它是否AnyObject &#34;答案是&#34;停止;你与as?走得太远。&#34;首先重新设计这个,这样你就不需要了,这通常意味着更早地弄清楚你的类型。

执行您尝试做的事情的正确方法通常是重载,而不是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传递给上述功能。您需要知道您正在使用的事物的类型。 (您对Firestore删除类型的评论表明您已经知道已经存在的类型,并且正在积极地抛弃这些类型并尝试稍后恢复它们。如果这是在这种情况下,上面的代码是完全正确的。不要丢弃类型。)

虽然有一些极端情况你无法知道这些类型(因为你真的在接受&#34;任何对象&#34;),在绝大多数情况下,不知道你的类型表明存在设计问题。

答案 1 :(得分:3)

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

首先,你不能用任何数组调用你的函数 - 数组,如struct s,不符合AnyObject(类做)。但是,你通过强制转换来调用函数 - 因为任何东西都可以转换为AnyObject(可能是通过将结构包装在简并类中),调用编译。

其次,你的问题推理是错误的 - 你说&#34;这表明对于一个空阵列,演员阵容失败。&#34; - 不是这样,演员两个案件中成功 ......

让我们看看实际传入的内容:

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"
)

(并且不打印演员&#34;成功&#34;)

它的长短是

A)力as! AnyObject有效地抛弃了数组类型并传递了类似于元素元组的元素,您的let _ = as?能够从类型中重新组合成一个数组元素。

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