2014-08-31 20 views
15
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错误是什么意思?协议类型的数组不能倒向具体类型数组


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

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的特例吗?

参考: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无关。

+0

我已经更新了我的答案 - 前一个显然是错的。 – Antonio 2014-09-01 08:25:09

+0

我还是不明白为什么'is'和'as?'操作符用于单个变量,但不适用于数组。但是现在可以通过'flagMap'解决。 – 2015-09-05 07:12:36

回答

16

忽略可选片刻结合和使用直接分配:报道

let x = ps as [X] 

以下运行时错误:

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

这意味着从方案的阵列,以使用者的阵列丧气需要obj-c绑定。这可以通过声明协议作为objc兼容迎刃而解:

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

有了简单的变化,现在的代码工作,并没有运行时异常。

现在如何是解决了,但留下为什么一个开放的问题。我还没有答案,但我会尽力深入研究。

附录:弄清楚“为什么”

我花了一些时间来调查这个问题,以下是我所配备。

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

protocol P {} 
class X : P {} 

我们创建P的阵列:到[X]作品

var array = [P]() 

转换的空数组:

array as [X] // 0 elements 

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

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

控制台输出说:

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

所以铸造协议中的对象的阵列,以其采用者的阵列需要桥接。这证明了为什么@objc解决了这个问题:

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

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

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

为了执行强制转换,运行时必须检查X是否符合P协议。该documentation明确指出:

您可以检查协议的一致性只有当你的协议标有@objc属性

要验证(不,我不信任的文档),我用在操场下面的代码:

protocol P {} 
class X : P {} 

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

但我得到一个不同的错误,指出:

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

写作,在“常规”项目,而不是我们得到了预期:

protocol P {} 
class X {} 

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

Cannot downcast from 'X' to [email protected] protocol type 'P' 

结论:为了使协议类型数组是垂头丧气的具体类型数组,该协议必须被打上@objc属性。原因在于运行时使用is运算符来检查协议一致性,因此文档仅适用于桥接协议。

+0

(关于你对现在被删除的问题的评论http://stackoverflow.com/questions/25589605/swift-forced-upwrapping-of-array-of-optionals:回答你自己的问题完全可以,人们鼓励他们分享他们的知识,参见http://stackoverflow.com/help/self-answer了解更多信息。) – 2014-08-31 07:29:39

+0

感谢@MartinR - 我一直看到有人发布像我这样的评论,所以我认为它是不允许。做了一次(可能太快的)搜索,找不到任何明确表示允许或不鼓励的事情。我尝试用我的apoligies与他联系... – Antonio 2014-08-31 07:48:53

+0

这是错误的。如果'A'是'B'的子类,'[A]'可以隐式转换为'[B]','[B]'可以明确地转换为'[A]'。'Int'和'UInt'都不是另一个的子类(事实上,它们都不是类,更不用说子类)。 – newacct 2014-09-01 06:14:34