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

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

问题描述

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是一个类,而不是一个协议,比code正常工作。
什么是类和协议之间的区别?他们俩都实现为堆指针,不是吗?
以上code能够成功编译,但在运行时崩溃。这是什么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,但我还是不明白这个样本code ++工程。

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?

引用:<一href=\"https://developer.apple.com/library/$p$prelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TypeCasting.html#//apple_ref/doc/uid/TP40014097-CH22-XID_498\">https://developer.apple.com/library/$p$prelease/ios/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

我想在这个操场code。由于这是纯粹的迅速code,我认为这无关@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}
}

通过简单的改变,code现在的工作,也没有运行时异常。

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的数组:

var array = [P]()

转换的空数组 [X]

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 的协议。在<一个href=\"https://developer.apple.com/library/$p$prelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-XID_413\">documentation明确指出:

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

要验证(不,我不信任的文档),我用这个code在操场上:

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 属性。其原因是,运行时将使用运算符来检查协议一致性,这给相应的文件仅适用于桥接协议。

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天全站免登陆