This repository has been archived by the owner on Dec 5, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 461
/
EXTSwizzle.h
193 lines (190 loc) · 7.21 KB
/
EXTSwizzle.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
* EXTSwizzle.h
* extobjc
*
* Created by Justin Spahr-Summers on 04.08.10.
* Released into the public domain.
*/
#import <objc/runtime.h>
#import <stdio.h>
#import "metamacros.h"
/**
* Replaces an instance method on \a CLASS, saving it under a new name.
* \a ORIGINAL specifies a selector name to replace with the implementation from
* \a NEW. Once replaced, the original implementation will be stored as selector
* \a RENAME. If \a RENAME is already in use, it will not be overwritten and the
* original method implementation will be lost!
*
* @code
* @implementation UIView (BetterDescription)
* - (NSString *)betterDescription {
* return [@"This description is better than " stringByAppendingString:
* [self oldDescription]];
* }
*
* + (void)load {
* EXT_SWIZZLE_INSTANCE_METHODS(
* UIView, // class
* description, // original method name
* betterDescription, // method to replace it with
* oldDescription // new name for the original
* );
* }
* @end
* @endcode
*
* @note This may not work as intended for class clusters.
*
* @warning This macro will allow you to swap methods with different argument
* and return types, but doing so could cause crashes, memory corruption, or
* other erratic behavior. Note that the compiler will not warn you if you do
* this by mistake!
*/
#define EXT_SWIZZLE_INSTANCE_METHODS(CLASS, ORIGINAL, NEW, RENAME) \
do { \
/*
* look up the target class by name
*/ \
Class cls_ = objc_getClass(metamacro_stringify(CLASS)); \
if (!cls_) { \
fprintf(stderr, "ERROR: no class %s exists\n", \
metamacro_stringify(CLASS) \
); \
break; \
} \
\
/*
* get a handle to the original method (the one to be moved out of the
* way)
*
* this should use class_getInstanceMethod() so that the "swizzling"
* still works even if implementations actually happen to be on
* a superclass – the only class that will actually be modified is CLASS
*/ \
Method orig_ = class_getInstanceMethod(cls_, @selector(ORIGINAL)); \
if (!orig_) { \
fprintf(stderr, "ERROR: class %s and superclasses do not contain an instance method for selector %s\n", \
metamacro_stringify(CLASS), \
metamacro_stringify(ORIGINAL) \
); \
break; \
} \
\
/*
* get a handle to the new method (the one to replace the original)
*/ \
Method new_ = class_getInstanceMethod(cls_, @selector(NEW)); \
if (!new_) { \
fprintf(stderr, "ERROR: class %s and superclasses do not contain an instance method for selector %s\n", \
metamacro_stringify(CLASS), \
metamacro_stringify(NEW) \
); \
break; \
} \
\
/*
* add a duplicate of the original method under the new name (which will
* effectively be a rename once the original method is replaced)
*/ \
IMP origImpl_ = method_getImplementation(orig_); \
if (!class_addMethod(cls_, @selector(RENAME), origImpl_, method_getTypeEncoding(orig_))) { \
fprintf(stderr, "ERROR: could not add instance method %s on %s\n", \
metamacro_stringify(RENAME), \
metamacro_stringify(CLASS) \
); \
break; \
} \
\
/*
* replace the original method's implementation with that of a new
* method (but never modifying any superclasses)
*/ \
IMP newImpl_ = method_getImplementation(new_); \
class_replaceMethod(cls_, @selector(ORIGINAL), newImpl_, method_getTypeEncoding(new_)); \
} while (0)
/**
* Replaces a class method on \a CLASS, saving it under a new name. \a ORIGINAL
* specifies a selector name to replace with the implementation from \a NEW.
* Once replaced, the original implementation will be stored as selector
* \a RENAME. If \a RENAME is already in use, it will not be overwritten and the
* original method implementation will be lost!
*
* @sa EXT_SWIZZLE_INSTANCE_METHODS
*
* @note This may not work as intended for class clusters.
*
* @warning This macro will allow you to swap methods with different argument
* and return types, but doing so could cause crashes, memory corruption, or
* other erratic behavior. Note that the compiler will not warn you if you do
* this by mistake!
*/
#define EXT_SWIZZLE_CLASS_METHODS(CLASS, ORIGINAL, NEW, RENAME) \
do { \
/*
* look up the target class by name
*/ \
Class cls_ = objc_getClass(metamacro_stringify(CLASS)); \
if (!cls_) { \
fprintf(stderr, "ERROR: no class %s exists\n", \
metamacro_stringify(CLASS) \
); \
break; \
} \
\
/*
* to deal with class methods, we actually need the metaclass (which is
* the class of the class object), upon which we will look up instance
* methods
*/ \
Class meta_ = object_getClass(cls_); \
\
/*
* get a handle to the original method (the one to be moved out of the
* way)
*
* this should use class_getInstanceMethod() so that the "swizzling"
* still works even if implementations actually happen to be on
* a superclass – the only class that will actually be modified is CLASS
*/ \
Method orig_ = class_getClassMethod(cls_, @selector(ORIGINAL)); \
if (!orig_) { \
fprintf(stderr, "ERROR: class %s and superclasses do not contain a class method for selector %s\n", \
metamacro_stringify(CLASS), \
metamacro_stringify(ORIGINAL) \
); \
break; \
} \
\
/*
* get a handle to the new method (the one to replace the original)
*/ \
Method new_ = class_getClassMethod(cls_, @selector(NEW)); \
if (!new_) { \
fprintf(stderr, "ERROR: class %s and superclasses do not contain a class method for selector %s\n", \
metamacro_stringify(CLASS), \
metamacro_stringify(NEW) \
); \
break; \
} \
\
/*
* add a duplicate of the original method under the new name (which will
* effectively be a rename once the original method is replaced)
*/ \
IMP origImpl_ = method_getImplementation(orig_); \
if (!class_addMethod(meta_, @selector(RENAME), origImpl_, \
method_getTypeEncoding(orig_))) { \
fprintf(stderr, "ERROR: could not add class method %s on %s\n", \
metamacro_stringify(RENAME), \
metamacro_stringify(CLASS) \
); \
break; \
} \
\
/*
* replace the original method's implementation with that of a new
* method (but never modifying any superclasses)
*/ \
IMP newImpl_ = method_getImplementation(new_); \
class_replaceMethod(meta_, @selector(ORIGINAL), newImpl_, method_getTypeEncoding(new_)); \
} while (0)