Swift里面怎么知道一个协议被多少类遵循

2,677 阅读1分钟

开发中想知道一个协议被多少类实现了。有没有方法知道呢。

protocol Animal {
    func speak()
}

class Cat:Animal {
    func speak() {
        print("meow")
    }
}

class Dog: Animal {
    func speak() {
        print("An An!")
    }
}

class Horse: Animal {
    func speak() {
        print("Hurrrr")
    }
}

在网上找能找到这几个方法

  • objc_copyProtocolList // 获取运行时知道的所有协议
  • protocol_copyProtocolList // 返回一个协议所采用的协议的数组。

上面两个方法,一个是知道有多少协议数组,一个是知道协议采用的协议数组。他们都没法知道一个协议被多少类实现。都没有达成目的。那就需要变通了。 反过来,找到这两个方法

  • objc_getClassList // 获取运行时知道的所有的类
  • class_conformsToProtocol // 获取一个类是否实现某个协议。

利用这两个方法,那就可以这样,先获取所有的类,然后遍历每个类是否实现了目标协议。代码如下:

func getClassesConformingProtocol() -> {
        let expectedClassCount = objc_getClassList(nil, 0)
        let allClasses = UnsafeMutablePointer<AnyClass>.allocate(capacity: Int(expectedClassCount))
        let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass>(allClasses)
        let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)

        for i in 0 ..< actualClassCount {
            let currentClass = allClasses[Int(i)]
            if class_conformsToProtocol(currentClass, Animal.Type) {
                print(currentClass)
            }
        }
}

这里有个问题,在Swiftclass_conformsToProtocol判断总是失败。

那就再变通,因为我们知道在swift中判断一个类是否遵循某个协议可以这样(aclass as? aprotocol)。代码修改如下:

func getClassesConformingProtocol() -> {
        let expectedClassCount = objc_getClassList(nil, 0)
        let allClasses = UnsafeMutablePointer<AnyClass>.allocate(capacity: Int(expectedClassCount))
        let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass>(allClasses)
        let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)

        for i in 0 ..< actualClassCount {
            let currentClass = allClasses[Int(i)]
            if let cls = currentClass as? Animal.Type {
                print(currentClass)
            }
        }
}

目标达成

当这部分代码被OC调用了,会出现这个错误。

 *** NSForwarding: warning: object 0x10bec81d0 of class 'Object' does not implement methodSignatureForSelector: -- trouble ahead
 *** NSForwarding: warning: object 0x10bec81d0 of class 'Object' does not implement doesNotRecognizeSelector: -- abort

解决方法也简单

func getClassesConformingProtocol() -> {
        let expectedClassCount = objc_getClassList(nil, 0)
        let allClasses = UnsafeMutablePointer<AnyClass>.allocate(capacity: Int(expectedClassCount))
        let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass>(allClasses)
        let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)

        for i in 0 ..< actualClassCount {
            let currentClass = allClasses[Int(i)]
            if (class_getInstanceMethod(currentClass, NSSelectorFromString("methodSignatureForSelector:")) != nil),

               (class_getInstanceMethod(currentClass, NSSelectorFromString("doesNotRecognizeSelector:")) != nil),
               let cls = currentClass as? Animal.Type {
                print(currentClass)
            }
        }
}

参考: stackoverflow.com/questions/3…