写在前面
开发中想知道一个协议被多少类实现了。有没有方法知道呢。
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)
}
}
}
这里有个问题,在Swift中class_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)
}
}
}