协议:为什么要求@ObjC进行一致性检查和可选要求?

时间:2014-11-13 13:25:45

标签: design-patterns swift programming-languages protocols optional

Swift documentation说明以下关于协议

  

只有标记了协议,才能检查协议一致性   使用@objc属性,如上面的HasArea协议所示。这个   attribute表示协议应该暴露给Objective-C   代码和使用Swift与Cocoa和Objective-C中描述。甚至   如果你没有与Objective-C互操作,你需要标记你的   具有@objc属性的协议,如果您希望能够检查   协议一致性。

     

另请注意,@ objc协议只能由类采用,而不能   通过结构或枚举。如果您将协议标记为@objc in   为了检查一致性,您可以申请   协议只对类类型。

  

只有在您的协议中才能指定可选的协议要求   标有@objc属性。即使你没有互操作性   使用Objective-C,您需要使用@objc标记协议   如果要指定可选要求,请使用属性。

     

另请注意,@ objc协议只能由类采用,而不能   通过结构或枚举。如果您将协议标记为@objc in   为了指定可选要求,您将只能申请   类协议的类协议。


为什么不能检查纯Swift协议(非@objc)的一致性。为什么他们没有可选要求?任何人都可以猜出潜在的语言设计原因吗?

我希望在未来某些未确定的(可能很远的地方)Apple将慢慢重新实现并用纯粹在Swift中编程的库替换 Cocoa CocoaTouch 。那时,如果我们不想避免使用任何与Obj-C相关的东西,我们是否应该避免在代码中使用可选的协议要求和协议检查一致性?

如果是这样,那么在不使用@objc的情况下实现类似模式的Swift惯用方法是什么? (例如,具有可选方法的代表。)


例如,使用非@objc协议无法实现此简单用例(PrintableDebugPrintableStreamable@objc

import UIKit

let firstName = "John"
let lastName = "Appleseed"
let age = 33
let height = 1.74

let values: [Any] = [firstName, lastName, age, height]
let stringifiedValues = [String]()

for value in values
{
    if let pritanbleValue = value as? Printable
    {
        stringifiedValues.append(value.description)
    }
    else if let debugPrintableValue = value as? DebugPrintable
    {
        stringifiedValues.append(value.debugDescription)
    }
    else if let streamableValue = value as? Streamable
    {
        var string = ""
        streamableValue.writeTo(&string)
        stringifiedValues.append(string)
    }
    // etc.    
    else 
    {
        stringifiedValues.append("[NoStringRepresentation]")
    }
}

2 个答案:

答案 0 :(得分:11)

Apple员工

jckarter Apple Developer Forums' thread上说了以下内容:

  

这是Swift运行时的限制。我们打算在将来的版本中删除此限制。

答案 1 :(得分:6)

Swift可能没有包含足够的运行时类型信息来检查协议一致性。协议实际上只是编译时的事情,ObjC在构建的产品中包含了额外的表,这些表对于运行程序来说并不是绝对必要的,以便确定一致性。由于@objc必须将类/协议转换为ObjC理解的内容,因此使用此属性声明的对象会获得额外的元数据以及其他适配器数据结构,因此ObjC不会注意到它们不是真正的ObjC。

Apple在ObjC中的建议一直是检查单个方法的存在,而不是查看类或协议,以允许鸭子类型并允许使用代理对象。我的猜测是,这就是为什么Swift的设计师认为在默认情况下省略协议运行时数据是可以的,以鼓励正确检测类的功能。

关于如何使用它:对于协议的情况,你显然应该使用?运算符(它检查对象是否存在以及它是否实现了给定的方法)。我不知道系统API是否同样适用,但在这种情况下它是一个ObjC类,你可以调用foo.respondsToSelector("doFoo:")