List of all subclasses of the same class

Is it possible to return a list of all subclasses of one class? For instance:

class Mother { } class ChildFoo: Mother { } class ChildBar: Mother { } let motherSubclasses = ... // TODO print(motherSubclasses) // should to return [ChildFoo.self, ChildBar.self] 
+7
source share
4 answers

Surprisingly, the Objective-C runtime functions just as well with Swift classes, even if they are not subclasses of NSObject . In addition, all classes in Swift appear to derive from SwiftObject . SwiftObject itself does not have a superclass.

Firstly, the wrapper structure for handling ObjC runtime functions:

 import Foundation struct ClassInfo : CustomStringConvertible, Equatable { let classObject: AnyClass let className: String init?(_ classObject: AnyClass?) { guard classObject != nil else { return nil } self.classObject = classObject! let cName = class_getName(classObject)! self.className = String(cString: cName) } var superclassInfo: ClassInfo? { let superclassObject: AnyClass? = class_getSuperclass(self.classObject) return ClassInfo(superclassObject) } var description: String { return self.className } static func ==(lhs: ClassInfo, rhs: ClassInfo) -> Bool { return lhs.className == rhs.className } } 

Here you can use it:

 class Mother { } class ChildFoo: Mother { } class ChildBar: Mother { } class AnIrrelevantClass { } let motherClassInfo = ClassInfo(Mother.self)! var subclassList = [ClassInfo]() var count = UInt32(0) let classList = objc_copyClassList(&count)! for i in 0..<Int(count) { if let classInfo = ClassInfo(classList[i]), let superclassInfo = classInfo.superclassInfo, superclassInfo == motherClassInfo { subclassList.append(classInfo) } } print(subclassList) 

This only does a minor search, so it won’t pick up the classes of grandchildren, but you get the idea.

+8
source

Here is an option based on the previous code, another answer, but shorter:

 func subclasses<T>(of theClass: T) -> [T] { var count: UInt32 = 0, result: [T] = [] let allClasses = objc_copyClassList(&count)! for n in 0 ..< count { let someClass: AnyClass = allClasses[Int(n)] guard let someSuperClass = class_getSuperclass(someClass), String(describing: someSuperClass) == String(describing: theClass) else { continue } result.append(someClass as! T) } return result } 

The type of the return value will match the type of the receiving variable, thanks to the generalization.

I wanted to write this as an AnyClass extension ... but unfortunately Swift does not allow this.

+1
source

Thanks, Jean. This is very useful with subclasses of func (theClass: T)

 let answer = subclasses(of: CIFilter.self) 

The answer has 282 subclasses of CIFilter in iOS 12.1.3. This is pretty much what is indicated on the documentation pages.

0
source

Optimized code version of Jean Le Moignan

  static func subclasses<T>(of theClass: T) -> [T] { var count: UInt32 = 0, result: [T] = [] let allClasses = objc_copyClassList(&count)! let classPtr = address(of: theClass) for n in 0 ..< count { let someClass: AnyClass = allClasses[Int(n)] guard let someSuperClass = class_getSuperclass(someClass), address(of: someSuperClass) == classPtr else { continue } result.append(someClass as! T) } return result } 
 public func address(of object: Any?) -> UnsafeMutableRawPointer{ return Unmanaged.passUnretained(object as AnyObject).toOpaque() } 

For each Type, there is only one instance of a metaclass at run time, so pointers to them are unique. For some reason, the === operator is not allowed for AnyClass, but we can directly compare pointers

performance test:

  let start = CFAbsoluteTimeGetCurrent() let found = RuntimeUtils.subclasses(of:UIViewController.self) let diff = CFAbsoluteTimeGetCurrent() - start print("Took \(diff) seconds, \(found.count) found") 

output:

String (description: theClass): Took 1.0465459823608398 seconds, 174 found

address (from: theClass): Took 0.2642860412597656 seconds, 174 found

0
source

Source: https://habr.com/ru/post/1265349/


All Articles