-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathObjCClass.swift
More file actions
154 lines (126 loc) · 4.69 KB
/
ObjCClass.swift
File metadata and controls
154 lines (126 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//
// ObjCClass.swift
//
//
// Created by Nick Randall on 27/12/2022.
//
import Foundation
import RuntimeFix
/// Wraps a runtime class.
public struct ObjCClass {
@usableFromInline let cls: AnyClass
/// Creates a wrapper of the specified class.
@inlinable public init(_ cls: AnyClass) {
self.cls = cls
}
/// Creates a wrapper of the named class.
/// - Returns: Returns `nil` if the class is not registered with the Objective-C runtime.
@inlinable public init?(named className: String) {
guard let cls = objc_lookUpClass(className) else {
return nil
}
self = ObjCClass(cls)
}
}
// MARK: - Basic attributes
public extension ObjCClass {
/// Returns the name of this class.
@inlinable var name: String {
class_getName(cls).asString
}
/// Returns the superclass of this class, or nil if this is a root class.
@inlinable var superclass: ObjCClass? {
class_getSuperclass(cls).map(ObjCClass.init)
}
/// Returns the size in bytes of instances of this class.
@inlinable var instanceSize: Int {
class_getInstanceSize(cls)
}
/// Returns the metaclass of this class.
@inlinable var metaClass: ObjCClass {
ObjCClass(class_getMetaclass(cls))
}
/// Returns a boolean value that indicates whether this class is a metaclass.
@inlinable var isMetaClass: Bool {
class_isMetaClass(cls)
}
/// Returns a boolean value that indicates whether this class is a root class.
@inlinable var isRootClass: Bool {
class_getSuperclass(cls) == nil
}
/// Returns the name of the dynamic library this class originated from.
/// - Returns: Returns `nil` if the class was created dynamically.
@inlinable var imageName: String? {
class_getImageName(cls)?.asString
}
/// The version number of this class definition.
@inlinable var version: Int {
get { Int(class_getVersion(cls)) }
nonmutating set { class_setVersion(cls, Int32(newValue)) }
}
}
// MARK: - Other classes
public extension ObjCClass {
/// A list of all the classes registered in the runtime.
@inlinable static var allClasses: RuntimeList<All> {
RuntimeList(All())
}
/// Returns all registered class definitions within a specified library or framework.
static func allClasses(in imageName: String) -> RuntimeList<Image> {
RuntimeList(Image(named: imageName))
}
/// Returns an array of the superclasses of this class up to but not including `topmostClass`.
/// - Parameter topmostClass: If set to `nil`, all superclasses are returned, even the root class.
/// - Parameter includeSelf: Whether the current class should be at the start of the returned array.
func superclasses(excluding topmostClass: ObjCClass? = nil, includeSelf: Bool = false) -> [ObjCClass] {
var superclasses: [ObjCClass] = []
var cls = self
while cls != topmostClass {
if cls != self || includeSelf {
superclasses.append(cls)
}
guard let superclass = cls.superclass else { break }
cls = superclass
}
return superclasses
}
/// Returns a list of all known subclasses of this class.
/// - Parameter directOnly: Whether to include direct subclasses only or to include all subclasses.
func subclasses(directOnly: Bool = false) -> some Sequence<ObjCClass> {
Self.allClasses.lazy.filter {
$0.isSubclass(of: self, directOnly: directOnly)
}
}
/// Returns a Boolean value that indicates whether the receiving class is a subclass of a given class.
/// - Parameter directOnly: Whether to test for a direct subclass relationship only or to also include ancestor superclasses.
func isSubclass(of other: ObjCClass, directOnly: Bool = false) -> Bool {
if directOnly {
return superclass == other
}
var supercls = superclass
while supercls != nil {
if supercls == other {
return true
}
supercls = supercls?.superclass
}
return false
}
}
extension ObjCClass: Equatable {
public static func == (lhs: ObjCClass, rhs: ObjCClass) -> Bool {
// Can't compare on cls itself, because the values returned from objc_copyClassList
// and from objc_lookUpClass for the same class are different
lhs.name == rhs.name
}
}
extension ObjCClass: CustomStringConvertible {
public var description: String {
"\(name): \(superclass?.name ?? "root class")"
}
}
extension ObjCClass: CustomDebugStringConvertible {
public var debugDescription: String {
"\(Self.self)(\(name))"
}
}