Skip to content

Commit 199f68f

Browse files
committed
Support strong, weak and unowned reference type while creating instance-based Interpose
1 parent 17853ad commit 199f68f

File tree

3 files changed

+95
-11
lines changed

3 files changed

+95
-11
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import Foundation
2+
3+
/// Container for AnyObject
4+
public class AnyObjectContainer {
5+
public var object: AnyObject {
6+
fatalError("Always override")
7+
}
8+
}
9+
10+
/// The container that hold a strong reference to the object
11+
internal class StrongObjectContainer: AnyObjectContainer {
12+
override var object: AnyObject {
13+
_object
14+
}
15+
16+
private let _object: AnyObject
17+
18+
init(_ object: AnyObject) {
19+
self._object = object
20+
}
21+
}
22+
23+
/// The container that hold a weak reference to the object
24+
internal class WeakObjectContainer: AnyObjectContainer {
25+
override var object: AnyObject {
26+
guard let object = _object else { fatalError("Bad Access") }
27+
return object
28+
}
29+
30+
private weak var _object: AnyObject?
31+
32+
init(_ object: AnyObject) {
33+
self._object = object
34+
}
35+
}
36+
37+
/// The container that hold an unowned reference to the object
38+
internal class UnownedObjectContainer: AnyObjectContainer {
39+
override var object: AnyObject { _object }
40+
41+
private unowned let _object: AnyObject
42+
43+
init(_ object: AnyObject) {
44+
self._object = object
45+
}
46+
}
47+
48+
extension AnyObjectContainer {
49+
/// Create a strong reference container
50+
public static func strong(_ object: AnyObject) -> AnyObjectContainer {
51+
StrongObjectContainer(object)
52+
}
53+
54+
/// Create a weak reference container
55+
public static func weak(_ object: AnyObject) -> AnyObjectContainer {
56+
WeakObjectContainer(object)
57+
}
58+
59+
/// Create an unowned reference container
60+
public static func unowned(_ object: AnyObject) -> AnyObjectContainer {
61+
UnownedObjectContainer(object)
62+
}
63+
}

Sources/InterposeKit/InterposeKit.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,18 @@ extension NSObject {
3030
/// Methods are hooked via replacing the implementation, instead of the usual exchange.
3131
/// Supports both swizzling classes and individual objects.
3232
final public class Interpose {
33+
3334
/// Stores swizzle hooks and executes them at once.
3435
public let `class`: AnyClass
3536
/// Lists all hooks for the current interpose class object.
3637
public private(set) var hooks: [AnyHook] = []
3738

3839
/// If Interposing is object-based, this is set.
39-
public let object: AnyObject?
40+
public var object: AnyObject? {
41+
objectContainer?.object
42+
}
43+
44+
internal let objectContainer: AnyObjectContainer?
4045

4146
// Checks if a object is posing as a different class
4247
// via implementing 'class' and returning something else.
@@ -58,7 +63,7 @@ final public class Interpose {
5863
/// If `builder` is present, `apply()` is automatically called.
5964
public init(_ `class`: AnyClass, builder: ((Interpose) throws -> Void)? = nil) throws {
6065
self.class = `class`
61-
self.object = nil
66+
self.objectContainer = nil
6267

6368
// Only apply if a builder is present
6469
if let builder = builder {
@@ -67,9 +72,16 @@ final public class Interpose {
6772
}
6873

6974
/// Initialize with a single object to interpose.
70-
public init(_ object: NSObject, builder: ((Interpose) throws -> Void)? = nil) throws {
71-
self.object = object
72-
self.class = type(of: object)
75+
public convenience init(_ object: NSObject, builder: ((Interpose) throws -> Void)? = nil) throws {
76+
try self.init(.strong(object), builder: builder)
77+
}
78+
79+
/// Initialize with a single object to interpose.
80+
public init(_ objectContainer: AnyObjectContainer, builder: ((Interpose) throws -> Void)? = nil) throws {
81+
self.objectContainer = objectContainer
82+
self.class = type(of: objectContainer.object)
83+
84+
let object = objectContainer.object
7385

7486
if let actualClass = checkObjectPosingAsDifferentClass(object) {
7587
if isKVORuntimeGeneratedClass(actualClass) {
@@ -106,8 +118,8 @@ final public class Interpose {
106118
_ implementation:(TypedHook<MethodSignature, HookSignature>) -> HookSignature?) throws -> TypedHook<MethodSignature, HookSignature> {
107119

108120
var hook: TypedHook<MethodSignature, HookSignature>
109-
if let object = self.object {
110-
hook = try ObjectHook(object: object, selector: selector, implementation: implementation)
121+
if let objectContainer = self.objectContainer {
122+
hook = try ObjectHook(objectContainer: objectContainer, selector: selector, implementation: implementation)
111123
} else {
112124
hook = try ClassHook(class: `class`, selector: selector, implementation: implementation)
113125
}

Sources/InterposeKit/ObjectHook.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,11 @@ extension Interpose {
4646
final public class ObjectHook<MethodSignature, HookSignature>: TypedHook<MethodSignature, HookSignature> {
4747

4848
/// The object that is being hooked.
49-
public let object: AnyObject
49+
public var object: AnyObject {
50+
objectContainer.object
51+
}
52+
53+
private let objectContainer: AnyObjectContainer
5054

5155
/// Subclass that we create on the fly
5256
var dynamicSubclass: AnyClass?
@@ -55,9 +59,14 @@ extension Interpose {
5559
let generatesSuperIMP = isSupportedArchitectureForSuper()
5660

5761
/// Initialize a new hook to interpose an instance method.
58-
public init(object: AnyObject, selector: Selector, implementation:(ObjectHook<MethodSignature, HookSignature>) -> HookSignature?) throws {
59-
self.object = object
60-
try super.init(class: type(of: object), selector: selector)
62+
public convenience init(object: AnyObject, selector: Selector, implementation:(ObjectHook<MethodSignature, HookSignature>) -> HookSignature?) throws {
63+
try self.init(objectContainer: .strong(object), selector: selector, implementation: implementation)
64+
}
65+
66+
/// Initialize a new hook to interpose an instance method.
67+
public init(objectContainer: AnyObjectContainer, selector: Selector, implementation:(ObjectHook<MethodSignature, HookSignature>) -> HookSignature?) throws {
68+
self.objectContainer = objectContainer
69+
try super.init(class: type(of: objectContainer.object), selector: selector)
6170
let block = implementation(self) as AnyObject
6271
replacementIMP = imp_implementationWithBlock(block)
6372
guard replacementIMP != nil else {

0 commit comments

Comments
 (0)