@@ -13,11 +13,39 @@ public final class Hook {
1313 build: @escaping HookBuilder < MethodSignature , HookSignature >
1414 ) throws {
1515 self . makeStrategy = { hook in
16+ // Hook should never be deallocated when invoking `makeHookIMP()`, as this only
17+ // occurs during strategy installation, which is triggered from a live hook instance.
18+ //
19+ // To ensure this, a strong reference cycle is intentionally created when the hook
20+ // is applied: the strategy installs a block-based IMP (stored in `appliedHookIMP`)
21+ // that retains a hook proxy, which in turn holds a strong reference to the hook.
22+ // This keeps the hook alive while it is applied, allowing access to its original
23+ // implementation.
24+ //
25+ // When not applied (i.e. prepared or reverted), `makeHookIMP` captures the hook
26+ // weakly to avoid premature retention, causing the hook to be deallocated when
27+ // the client releases it.
28+ //
29+ // Reference graph:
30+ // +------------------+
31+ // | Client |
32+ // +------------------+
33+ // |
34+ // v
35+ // +------------------+ +------------------+ weak
36+ // | HookProxy |---->| Hook |< - - - - - - -+
37+ // +------------------+ +------------------+ |
38+ // ^ | |
39+ // | v |
40+ // | +------------------+ +------------------+
41+ // | | HookStrategy |---->| makeHookIMP |
42+ // | +------------------+ +------------------+
43+ // | |
44+ // | v
45+ // | +------------------+
46+ // +--------------| appliedHookIMP |
47+ // +------------------+
1648 let makeHookIMP : ( ) -> IMP = { [ weak hook] in
17-
18- // Hook should never be deallocated when invoking `makeHookIMP()`, as this only
19- // happens when installing implementation from within the strategy, which is
20- // triggered from a live hook instance.
2149 guard let hook else {
2250 Interpose . fail (
2351 """
0 commit comments