How do you inspect a class’s methods and properties at run time, and what does a Swift class show?
Predict first
Which selectors does the runtime list for this class? (sorted)
final class Shape: NSObject {
@objc var sides = 3
@objc func area(scale: Double) -> Double { scale }
@objc dynamic func name() -> String { "shape" }
func hidden() {}
var swiftOnly = 1
private func secret() {}
}
var n: UInt32 = 0
let list = class_copyMethodList(Shape.self, &n)!
print((0..<Int(n)).map { NSStringFromSelector(method_getName(list[$0])) }.sorted())
- ["area", "name", "sides"]
- ["areaWithScale:", "init", "name", "setSides:", "sides"]
- all seven members
Mental model. It is reading the phone book of a building: only departments that registered a number are listed, and a department with an internal extension never appears.
The runtime functions class_copyMethodList, class_copyPropertyList, class_copyIvarList and class_copyProtocolList return C arrays you free afterwards, and method_getName with NSStringFromSelector turns an entry into text. class_getSuperclass and object_getClass walk the hierarchy; the latter returns the real isa, which differs from the declared class for a KVO-observed object. Debuggers, serializers, mocking and “find the private method” investigations all use these calls.
From Swift you only see what was exposed to the runtime. For a Swift class that inherits NSObject, the list contains @objc members (methods with their Objective-C selector, such as areaWithScale:, plus accessors for @objc properties) and init, but not members that are plain Swift, private or generic. A pure Swift class without NSObject has no useful Objective-C metadata at all. The class name of a Swift class is module-qualified (Module.Shape), which matters when you look a class up by string.
The Swift label becomes part of the selector (areaWithScale:), a property gives a getter and a setter, and only @objc members appear.
Common traps
- Forgetting to
freethe returned array. It leaks on every call. - Expecting to see Swift-only members. They are not in the method table, so absence is not proof a method does not exist.
They might follow up with
- How do you find the real class of a KVO-observed object?
object_getClass;type(of:)and.classmay hide the subclass. - Is this safe to ship? Reading is, but calling private selectors found this way can be rejected in App Review.
Related questions
- 4.1.4 What is isa, and why does your object’s runtime class sometimes differ from its Swift type?
- 4.4.2 Which Swift features cannot be seen from Objective-C?
- 4.4.1 What do @objc, @objcMembers, @objc(name), dynamic and @nonobjc each do?
What is a method type encoding, and why does a wrong signature crash… →