【问题标题】:How to list all classes conforming to protocol in Swift?如何在 Swift 中列出所有符合协议的类?
【发布时间】:2023-03-03 07:02:24
【问题描述】:

如何列出所有在 Swift 中实现给定协议的类?

假设我们有一个例子:

protocol Animal {
    func speak()
}

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

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

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

这是我目前(不可编译)的方法:

func getClassesImplementingProtocol(p: Protocol) -> [AnyClass] {
    let classes = objc_getClassList()
    var ret = [AnyClass]()

    for cls in classes {
        if class_conformsToProtocol(cls, p) {
            ret.append(cls)
        }
    }
    return ret
}

func objc_getClassList() -> [AnyClass] {
    let expectedClassCount = objc_getClassList(nil, 0)
    let allClasses = UnsafeMutablePointer<AnyClass?>.alloc(Int(expectedClassCount))
    let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
    let actualClassCount:Int32 = objc_getClassList(autoreleasingAllClasses, expectedClassCount)

    var classes = [AnyClass]()
    for i in 0 ..< actualClassCount {
        if let currentClass: AnyClass = allClasses[Int(i)] {
            classes.append(currentClass)
        }
    }

    allClasses.dealloc(Int(expectedClassCount))

    return classes
}

但是当调用任何一个时

getClassesImplementingProtocol(Animal.Protocol)

getClassesImplementingProtocol(Animal)

getClassesImplementingProtocol(Animal.self)

导致 Xcode 错误:无法将类型 (Animal.Protocol).Type 的值转换为预期的参数类型“协议”。

有人设法让这个工作正常吗?

【问题讨论】:

标签: ios swift reflection swift2


【解决方案1】:

由于您使用 Objective-C 运行时来获取类型自省,因此您需要以这种方式将 @objc 添加到您的代码中:

@objc protocol Animal {
  func speak()
}

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

class Dog: Animal {
  @objc func speak() {
    print("Av Av!")
  }
}

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

请注意,这种类型的自省可能会很慢。

【讨论】:

  • 感谢布鲁诺,它有效。关于性能,我只是测量了它:在 iPad3(我认为它属于慢速设备)上,getClassesImplementingProtocol 调用需要大约 115 毫秒,而在 iPhone6S 上需要 12 毫秒。对我来说,这是在应用启动时执行一次,因此性能不是关键问题。
  • 没有我预期的那么慢,所以这是件好事!我很高兴它对你有用。
  • 有什么方法可以得到类似结构的东西吗?
【解决方案2】:

就速度而言,您可以在一个 for 中完成,而不必像这样遍历所有类:

func getClassesConformingProtocol(p: Protocol)-> [AnyClass]{
        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)

        var classes = [AnyClass]()
        for i in 0 ..< actualClassCount {
            let currentClass = allClasses[Int(i)]
            if class_conformsToProtocol(currentClass, p) {
                classes.append(currentClass)
            }
        }

        return classes

}

【讨论】:

  • 请注意,这种方式似乎不再适用于 Xcode 11.4。似乎新 SDK 出现了问题,并且从“getClassList”返回的类充满了无效的类。 bugs.swift.org/browse/SR-12344
  • 是的,这在 11.4-beta 版本中被破坏/报告,并且仍未修复。与快速保留有关。解决方法对我也不起作用,但不要被愚弄,您可以使用解决方法打印类,但是如果您尝试将其添加到数组中,那么崩溃又回来了......然后我哭了一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-19
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多