协议类型数组不能向下转换为具体类型数组 [英] protocol typed array can't be downcast to concrete type array

查看:36
本文介绍了协议类型数组不能向下转换为具体类型数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

protocol P : class {
    var value:Int {get}
}

class X : P {
    var value = 0

    init(_ value:Int) {
        self.value = value
    }
}

var ps:[P] = [X(1), X(2)]
for p in ps {
    if let x = p as? X {   // works for a single variable
        ...
    }
}

if let xs = ps as? [X] {   // doesn't work for an array (EXC_BAD_ACCESS)
    ...
}

如果 P 是一个类而不是一个协议,那么代码可以正常工作.类和协议有什么区别?它们都作为堆中的指针实现,不是吗?上面的代码可以编译成功,但是运行时崩溃.这个 EXC_BAD_ACCESS 错误是什么意思?

If P is a class instead of a protocol, than the code works correctly. What's the difference between class and protocol? They're both implemented as pointers in the heap, aren't they? The above code can be compiled successfully, but crash at runtime. What does this EXC_BAD_ACCESS error mean?

感谢@Antonio,但我还是不明白这个示例代码是如何工作的.

Thanks to @Antonio, but I still don't understand how this sample code works.

let someObjects: [AnyObject] = [
    Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"),
    Movie(name: "Moon", director: "Duncan Jones"),
    Movie(name: "Alien", director: "Ridley Scott")
]
for movie in someObjects as [Movie] {
    println("Movie: '\(movie.name)', dir. \(movie.director)")
}

AnyObject 是特例吗?

Is AnyObject a special case?

参考:https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_498

protocol P {

}

@objc class X : P {

}

@objc class Y : X {

}

var xs:[X] = [Y(), Y()]
var ps:[P] = [Y(), Y()]


xs as? [Y]  // works
ps as? [Y]  // EXC_BAD_ACCESS

我在操场上试过这段代码.由于这是纯swift代码,我认为它与@objc无关.

I tried this code in playground. Since this is pure swift code, I think it has nothing to do with @objc.

推荐答案

暂时忽略可选绑定并使用直接赋值:

Ignoring the optional binding for a moment and using a direct assignment:

let x = ps as [X]

报告了以下运行时错误:

the following runtime error is reported:

fatal error: array element cannot be bridged to Objective-C

这意味着从协议数组到采用者数组的向下转换需要 obj-c 绑定.这可以通过将协议声明为 objc 兼容来轻松解决:

That means the downcast from array of protocols to array of adopters requires obj-c binding. This can be easily solved by declaring the protocol as objc compatible:

@objc protocol P : class {
    var value:Int {get}
}

通过这个简单的更改,代码现在可以工作并且不会引发运行时异常.

With that simple change, the code now works and no run time exception is raised.

现在如何解决了,但为什么是一个悬而未决的问题.我还没有答案,但我会尝试深入挖掘.

Now the how is solved, but leaving the why an open issue. I don't have an answer yet, but I'll try to dig deeper on that.

附录:找出为什么"

我花了一些时间调查这个问题,以下是我的结论.

I spent some time investigating on this issue, and following is what I've come with.

我们有一个协议和一个采用它的类:

We have a protocol and a class adopting it:

protocol P {}
class X : P {}

我们创建一个 P 数组:

We create an array of P:

var array = [P]()

将空数组转换为 [X] 有效:

Converting the empty array to [X] works:

array as [X] // 0 elements

如果我们向数组中添加一个元素,就会发生运行时错误:

If we add an element to the array, a runtime error occurs:

array.append(X())
array as [X] // Execution was interrupted, reason: ...

控制台输出说:

fatal error: array element cannot be bridged to Objective-C

因此,将协议对象数组转换为其采用者数组需要桥接.这证明了为什么@objc 解决了这个问题:

So casting an array of protocol objects to an array of its adopter requires bridging. That justifies why @objc fixes the issue:

@objc protocol P {}
class X : P {}

var array = [P]()
array.append(X())
array as [X] // [X]

筛选文档,我找到了发生这种情况的原因.

Sifting the documentation, I found out the reason for that to happen.

为了执行转换,运行时必须检查X 是否符合P 协议.文档 明确指出:

In order to perform the cast, the runtime has to check whether X conforms to the P protocol. The documentation clearly states that:

只有当你的协议标有@objc 属性时,你才能检查协议的一致性

You can check for protocol conformance only if your protocol is marked with the @objc attribute

为了验证(不是我不相信文档),我在操场上使用了此代码:

To verify that (not that I don't trust the documentation), I've used this code in the playground:

protocol P {}
class X : P {}

let x = X()
let y = x is P

但我得到一个不同的错误,说明:

but I get a different error, stating that:

Playground execution failed: <EXPR>:18:11: error: 'is' test is always true 
let y = x is P

将其写入常规"项目中,我们得到了预期的结果:

Writing that in a "regular" project instead we get what expected:

protocol P {}
class X {}

func test() {
    let x = X()
    let y = x is P
}

Cannot downcast from 'X' to non-@objc protocol type 'P'

结论:为了将协议类型数组向下转换为具体类型数组,协议必须用 @objc 属性标记.原因是运行时使用 is 运算符来检查协议一致性,因此文档仅适用于桥接协议.

Conclusion: in order for a protocol typed array to be downcast to a concrete type array, the protocol must be marked with the @objc attribute. The reason is that the runtime uses the is operator to check for protocol conformance, which accordingly to the documentation is only available for bridged protocols.

这篇关于协议类型数组不能向下转换为具体类型数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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