前段时间在程序里遇到一个bug, 实例代码如下:

class B {
    let content: [Any] // 因为考虑可能会接受tuple, 所以用了Any
    init(content: [Any]) {
        self.content = content
    }
}
let obj = [NSObject()]
B(content: obj.map { $0 })  // 1. 不报错
B(content: obj) // 2. 运行时异常: array cannot be bridged from Objective-C

1不报错应该容易理解, 因为Swift经过类型推断, map传入参数的返回值类型为Any, map函数的返回值为[Any], 类型正确. 2错误提示是array不能从OC被桥接, 发生在我把[NSObject]类型传递给[Any]的时候. 难道是Cocoa的类和Swift的Protocol之间不能交互? 于是做了下面的实验:

protocol P {
    
}
class PImpl: P {
    
}
let pi = [PImpl()]
let p: [P] = pi // 运行时异常: array cannot be bridged from Objective-C
let op: [AnyObject] = obj       // 不报错

Protocol PAny出现了同样的错误, 但是AnyObject却没有问题. 那P, AnyAnyObject的区别是什么呢? 我们看下定义:

// P: 
protocol P

// Any:
public typealias Any = protocol<>

// AnyObject:
@objc public protocol AnyObject

忽略public之后, 唯一的区别就是AnyObject多了一个@objc. 那么如果给P加上@objc会不会就不会报错了?

@objc protocol PAgain {
    
}
class PImplAgain: PAgain {
    
}
let pia = [PImplAgain()]
let pa: [PAgain] = pia    // 不报错!

看来果然是@objc的问题. 那这个货到底干了啥呢? 我们看下官方文档的解释:

Apply this attribute to any declaration that can be represented in Objective-C—for example, non-nested classes, protocols, nongeneric enumerations (constrained to integer raw-value types), properties and methods (including getters and setters) of classes and protocols, initializers, deinitializers, and subscripts. The objc attribute tells the compiler that a declaration is available to use in Objective-C code.

Classes marked with the objc attribute must inherit from a class defined in Objective-C. If you apply the objc attribute to a class or protocol, it’s implicitly applied to the Objective-C compatible members of that class or protocol. The compiler also implicitly adds the objc attribute to a class that inherits from another class marked with the objc attribute or a class defined in Objective-C. Protocols marked with the objc attribute can’t inherit from protocols that aren’t.

@objc的意思是, 所有用这个属性标识的thing都能在OC里使用. 编译器应该会对这些thing做一些处理, 使得OC能够使用. 基于这些实验, 对上述问题原因的猜测解释: 数组在runtime底层的转换工作实际上都是OC再处理的. 如果一个Swift的Protocol没有@objc标记, 那么runtime处理是OC就不认识它, 就没办法处理. 至于为啥没有@objc标记的Protocol就不能处理, 我猜是因为Swift的Protocol可以被Struct和Enum继承的, 但是Struct和Enum是不能出现在NSArray中的.

个人理解, 出错请指正

更详细的实现可以参考so上一个答案: link