Skip to content

Commit 2957b35

Browse files
committed
More refactoring in ObjectSubclassManager
1 parent f90a553 commit 2957b35

File tree

2 files changed

+26
-13
lines changed

2 files changed

+26
-13
lines changed

Sources/InterposeKit/Hooks/HookStrategy/ObjectHookStrategy/ObjectSubclassManager.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,17 +36,16 @@ internal enum ObjectSubclassManager {
3636
// with the runtime.
3737
let subclass: AnyClass = try self.makeSubclass(for: object)
3838

39-
// Then, set the created class on the object.
40-
object_setClass(object, subclass)
39+
// Finally, set the created class on the object.
40+
let previousClass: AnyClass? = object_setClass(object, subclass)
4141

4242
Interpose.log({
4343
let subclassName = NSStringFromClass(subclass)
4444
let objectAddress = String(format: "%p", object)
4545
var message = "Created subclass: \(subclassName) for object \(objectAddress)"
4646

47-
if let superclass = class_getSuperclass(subclass) {
48-
let superclassName = NSStringFromClass(superclass)
49-
message += " (was: \(superclassName))"
47+
if let previousClass {
48+
message += " (previously: \(NSStringFromClass(previousClass)))"
5049
}
5150

5251
return message
@@ -74,14 +73,18 @@ internal enum ObjectSubclassManager {
7473
let subclassName = self.uniqueSubclassName(for: perceivedClass)
7574

7675
return try subclassName.withCString { cString in
76+
// Attempt to allocate a new subclass that inherits from the object’s actual class.
7777
guard let subclass: AnyClass = objc_allocateClassPair(actualClass, cString, 0) else {
78-
throw InterposeError.failedToAllocateClassPair(
79-
class: perceivedClass,
80-
subclassName: subclassName
78+
throw InterposeError.subclassCreationFailed(
79+
subclassName: subclassName,
80+
object: object
8181
)
8282
}
8383

84+
// Set the perceived class to make the runtime report the original type.
8485
class_setPerceivedClass(for: subclass, to: perceivedClass)
86+
87+
// Register the subclass with the runtime.
8588
objc_registerClassPair(subclass)
8689

8790
return subclass

Sources/InterposeKit/InterposeError.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,19 @@ public enum InterposeError: LocalizedError {
4444
selector: Selector,
4545
imp: IMP?
4646
)
47-
48-
/// Unable to register subclass for object-based interposing.
49-
case failedToAllocateClassPair(class: AnyClass, subclassName: String)
47+
48+
/// Failed to create a dynamic subclass for the given object.
49+
///
50+
/// This can occur if the desired subclass name is already in use. While InterposeKit
51+
/// generates globally unique subclass names using an internal counter, a name collision may
52+
/// still happen if another system has registered a class with the same name earlier during
53+
/// the process lifetime.
54+
case subclassCreationFailed(
55+
subclassName: String,
56+
object: NSObject
57+
)
58+
59+
// ---
5060

5161
/// Object-based hooking does not work if an object is using KVO.
5262
/// The KVO mechanism also uses subclasses created at runtime but doesn't check for additional overrides.
@@ -81,8 +91,8 @@ extension InterposeError: Equatable {
8191
return "Implementation not found: -[\(`class`) \(selector)]"
8292
case .revertCorrupted(let `class`, let selector, let IMP):
8393
return "Unexpected Implementation in -[\(`class`) \(selector)]: \(String(describing: IMP))"
84-
case .failedToAllocateClassPair(let `class`, let subclassName):
85-
return "Failed to allocate class pair: \(`class`), \(subclassName)"
94+
case .subclassCreationFailed(let subclassName, let object):
95+
return "Failed to allocate class pair: \(object), \(subclassName)"
8696
case .kvoDetected(let obj):
8797
return "Unable to hook object that uses Key Value Observing: \(obj)"
8898
case .objectPosingAsDifferentClass(let obj, let actualClass):

0 commit comments

Comments
 (0)