forked from wix-incubator/DTXObjectiveCHelpers
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DTXSwizzlingHelper.h
147 lines (119 loc) · 4.25 KB
/
DTXSwizzlingHelper.h
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
//
// DTXSwizzlingHelper.h
// DTXObjectiveCHelpers
//
// Created by Leo Natan (Wix) on 12/4/19.
//
#ifndef DTXSwizzlingHelper_h
#define DTXSwizzlingHelper_h
#if __OBJC__
#import <objc/runtime.h>
#ifdef DEBUG
#define SWIZ_TRAP() raise(SIGTRAP);
#else
#define SWIZ_TRAP()
#endif
#define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \
NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \
NSLog(@"%@", errStr); \
SWIZ_TRAP() \
if (ERROR_VAR) { \
*ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \
code:-1 \
userInfo:@{NSLocalizedDescriptionKey:errStr}]; \
}
#define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__)
#define GetClass(obj) object_getClass(obj)
#ifndef DTX_ALWAYS_INLINE
#define DTX_ALWAYS_INLINE inline __attribute__((__always_inline__))
#endif /* DTX_ALWAYS_INLINE */
DTX_ALWAYS_INLINE
static BOOL DTXSwizzleMethod(Class cls, SEL orig, SEL alt, NSError** error)
{
Method origMethod = class_getInstanceMethod(cls, orig);
if (!origMethod) {
SetNSError(error, @"original method %@ not found for class %@", NSStringFromSelector(orig), cls);
return NO;
}
Method altMethod = class_getInstanceMethod(cls, alt);
if (!altMethod) {
SetNSError(error, @"alternate method %@ not found for class %@", NSStringFromSelector(alt), cls);
return NO;
}
class_addMethod(cls, orig, class_getMethodImplementation(cls, orig), method_getTypeEncoding(origMethod));
class_addMethod(cls, alt, class_getMethodImplementation(cls, alt), method_getTypeEncoding(altMethod));
method_exchangeImplementations(class_getInstanceMethod(cls, orig), class_getInstanceMethod(cls, alt));
return YES;
}
DTX_ALWAYS_INLINE
static BOOL DTXSwizzleClassMethod(Class cls, SEL orig, SEL alt, NSError** error)
{
return DTXSwizzleMethod(GetClass((id)cls), orig, alt, error);
}
DTX_ALWAYS_INLINE
static void __DTXCopyMethods(Class orig, Class target)
{
//Copy class methods
Class targetMetaclass = object_getClass(target);
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(object_getClass(orig), &methodCount);
for (unsigned int i = 0; i < methodCount; i++)
{
Method method = methods[i];
if(strcmp(sel_getName(method_getName(method)), "load") == 0 || strcmp(sel_getName(method_getName(method)), "initialize") == 0)
{
continue;
}
class_addMethod(targetMetaclass, method_getName(method), method_getImplementation(method), method_getTypeEncoding(method));
}
free(methods);
//Copy instance methods
methods = class_copyMethodList(orig, &methodCount);
for (unsigned int i = 0; i < methodCount; i++)
{
Method method = methods[i];
class_addMethod(target, method_getName(method), method_getImplementation(method), method_getTypeEncoding(method));
}
free(methods);
}
DTX_ALWAYS_INLINE
static BOOL DTXDynamicallySubclass(id obj, Class target)
{
SEL canarySEL = NSSelectorFromString([NSString stringWithFormat:@"__dtx_canaryInTheCoalMine_%@", NSStringFromClass(target)]);
if([obj respondsToSelector:canarySEL])
{
//Already there.
return YES;
}
NSString* clsName = [NSString stringWithFormat:@"%@(%@)", NSStringFromClass(object_getClass(obj)), NSStringFromClass(target)];
Class cls = objc_getClass(clsName.UTF8String);
if(cls == nil)
{
cls = objc_allocateClassPair(object_getClass(obj), clsName.UTF8String, 0);
__DTXCopyMethods(target, cls);
class_addMethod(cls, canarySEL, imp_implementationWithBlock(^ (id _self) {}), "v16@0:8");
objc_registerClassPair(cls);
}
NSMutableDictionary* superRegistrar = objc_getAssociatedObject(obj, (void*)&objc_setAssociatedObject);
if(superRegistrar == nil)
{
superRegistrar = [NSMutableDictionary new];
}
superRegistrar[NSStringFromClass(target)] = object_getClass(obj);
object_setClass(obj, cls);
objc_setAssociatedObject(obj, (void*)&objc_setAssociatedObject, superRegistrar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
return YES;
}
DTX_ALWAYS_INLINE
static Class DTXDynamicSubclassSuper(id obj, Class dynamic)
{
NSMutableDictionary* superRegistrar = objc_getAssociatedObject(obj, (void*)&objc_setAssociatedObject);
Class cls = superRegistrar[NSStringFromClass(dynamic)];
if(cls == nil)
{
cls = class_getSuperclass(object_getClass(obj));
}
return cls;
}
#endif /* __OBJC__ */
#endif /* DTXSwizzlingHelper_h */