如何列出一个对象符合的协议?

7

使用 Objective-C 运行时,我可以获取一个对象符合的所有 @objc 协议列表:

let obj = NSObject()

var pc: UInt32 = 0
let plist = class_copyProtocolList(object_getClass(obj), &pc)

print("\(obj.dynamicType) conforms to \(pc) protocols")

for i in 0 ..< Int(pc) {
    print(String(format: "Protocol #%d: %s", arguments: [i, protocol_getName(plist[i])]))
}

或者运行时加载的所有Objective-C协议:

var allProtocolCount: UInt32 = 0

let protocols = objc_copyProtocolList(&allProtocolCount)

print("\(allProtocolCount) total protocols")

for i in 0 ..< Int(allProtocolCount) {
    print(String(format: "Protocol #%d: %s", arguments: [i, protocol_getName(protocols[i])]))
}

但是这两个都没有列出任何 Swift 协议:
func == (lhs: MyClass, rhs: MyClass) -> Bool {
    return lhs.value == rhs.value
}

class MyClass: Equatable, Hashable {

    var value: Int
    var hashValue: Int {
        return value
    }

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

var count: UInt32 = 0;

let strProtocols = class_copyProtocolList(MyClass.self, &count) // 0x0000000000000000

strProtocols的值为0,但我期望它应该返回sizeof(Protocol) * 2(因为MyClass遵循了EquatableHashable协议)。

运行时是否提供了一个接口来获取对象所遵循的协议列表?

1个回答

4
你无法这样做。不是 ObjC 协议的 Swift 协议仅存在于编译时,并不存在于对象本身(这就是为什么它们的方法基于变量类型静态分派)。

抱歉我的无礼,但这个答案就像是一堆词语拼凑而成的。编译器知道的。 - original_username
2
重点在于,虽然编译器知道运行时的情况,但是这些信息并没有被捕获到编译二进制文件中,因此您无法查询特定对象的信息。我没有关注最近有关反射的发展,因此这方面的一些内容在过去的18个月内可能已经发生了变化。 - Joseph Lord
谢谢。您的评论很有价值,可以作为一个不错的回答 :) 我误解了您原来的回答,认为“Swift不会保留协议一致性的数据库”,这在考虑到Swift的编译时检查时并不合理。 - original_username
不可能是正确的,因为你需要在运行时进行类型检查。当你使用 if let x as Protocol 时,运行时会检查一致性。仅仅因为 ObjC 运行时看不到 Swift 协议,这并不意味着元数据不存在。你只需要使用来自 Swift 运行时的不同 API。 - Léo Natan
对于 as?as! 强制转换,你可能是正确的(纯 as 转换在编译时进行检查并将被优化或映射到桥接)。对于 as?as!,它可能需要以某种方式知道,尽管很可能是类型名称而不是您所知道的协议名称。我现在只是猜测。我们可以检查源代码(我认为在2016年回答时不可能)。 - Joseph Lord
思考一下@LeoNatan,你的例子中x是什么?如果它是具体类型,那么它会告诉你它应该是一个“as”转换,这将在编译时进行检查,因此这是关于x是不同协议类型的情况,可能符合协议,也可能不符合。现在作为传递的协议类型,可能会有额外的类型信息,对类型本身的引用可以检查符合性,这是你可能无法从Swift类或至少结构中获得的。 - Joseph Lord

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接