RSSwizzle.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. //
  2. // RSSwizzle.h
  3. // RSSwizzleTests
  4. //
  5. // Created by Yan Rabovik on 05.09.13.
  6. //
  7. //
  8. #if __has_feature(modules)
  9. @import Foundation;
  10. #else
  11. #import <Foundation/Foundation.h>
  12. #endif
  13. #pragma mark - Macros Based API
  14. /// A macro for wrapping the return type of the swizzled method.
  15. #define RSSWReturnType(type) type
  16. /// A macro for wrapping arguments of the swizzled method.
  17. #define RSSWArguments(arguments...) _RSSWArguments(arguments)
  18. /// A macro for wrapping the replacement code for the swizzled method.
  19. #define RSSWReplacement(code...) code
  20. /// A macro for casting and calling original implementation.
  21. /// May be used only in RSSwizzleInstanceMethod or RSSwizzleClassMethod macros.
  22. #define RSSWCallOriginal(arguments...) _RSSWCallOriginal(arguments)
  23. #pragma mark └ Swizzle Instance Method
  24. /**
  25. Swizzles the instance method of the class with the new implementation.
  26. Example for swizzling `-(int)calculate:(int)number;` method:
  27. @code
  28. RSSwizzleInstanceMethod(classToSwizzle,
  29. @selector(calculate:),
  30. RSSWReturnType(int),
  31. RSSWArguments(int number),
  32. RSSWReplacement(
  33. {
  34. // Calling original implementation.
  35. int res = RSSWCallOriginal(number);
  36. // Returning modified return value.
  37. return res + 1;
  38. }), 0, NULL);
  39. @endcode
  40. Swizzling frequently goes along with checking whether this particular class (or one of its superclasses) has been already swizzled. Here the `RSSwizzleMode` and `key` parameters can help. See +[RSSwizzle swizzleInstanceMethod:inClass:newImpFactory:mode:key:] for details.
  41. Swizzling is fully thread-safe.
  42. @param classToSwizzle The class with the method that should be swizzled.
  43. @param selector Selector of the method that should be swizzled.
  44. @param RSSWReturnType The return type of the swizzled method wrapped in the RSSWReturnType macro.
  45. @param RSSWArguments The arguments of the swizzled method wrapped in the RSSWArguments macro.
  46. @param RSSWReplacement The code of the new implementation of the swizzled method wrapped in the RSSWReplacement macro.
  47. @param RSSwizzleMode The mode is used in combination with the key to indicate whether the swizzling should be done for the given class. You can pass 0 for RSSwizzleModeAlways.
  48. @param key The key is used in combination with the mode to indicate whether the swizzling should be done for the given class. May be NULL if the mode is RSSwizzleModeAlways.
  49. @return YES if successfully swizzled and NO if swizzling has been already done for given key and class (or one of superclasses, depends on the mode).
  50. */
  51. #define RSSwizzleInstanceMethod(classToSwizzle, \
  52. selector, \
  53. RSSWReturnType, \
  54. RSSWArguments, \
  55. RSSWReplacement, \
  56. RSSwizzleMode, \
  57. key) \
  58. _RSSwizzleInstanceMethod(classToSwizzle, \
  59. selector, \
  60. RSSWReturnType, \
  61. _RSSWWrapArg(RSSWArguments), \
  62. _RSSWWrapArg(RSSWReplacement), \
  63. RSSwizzleMode, \
  64. key)
  65. #pragma mark └ Swizzle Class Method
  66. /**
  67. Swizzles the class method of the class with the new implementation.
  68. Example for swizzling `+(int)calculate:(int)number;` method:
  69. @code
  70. RSSwizzleClassMethod(classToSwizzle,
  71. @selector(calculate:),
  72. RSSWReturnType(int),
  73. RSSWArguments(int number),
  74. RSSWReplacement(
  75. {
  76. // Calling original implementation.
  77. int res = RSSWCallOriginal(number);
  78. // Returning modified return value.
  79. return res + 1;
  80. }));
  81. @endcode
  82. Swizzling is fully thread-safe.
  83. @param classToSwizzle The class with the method that should be swizzled.
  84. @param selector Selector of the method that should be swizzled.
  85. @param RSSWReturnType The return type of the swizzled method wrapped in the RSSWReturnType macro.
  86. @param RSSWArguments The arguments of the swizzled method wrapped in the RSSWArguments macro.
  87. @param RSSWReplacement The code of the new implementation of the swizzled method wrapped in the RSSWReplacement macro.
  88. */
  89. #define RSSwizzleClassMethod(classToSwizzle, \
  90. selector, \
  91. RSSWReturnType, \
  92. RSSWArguments, \
  93. RSSWReplacement) \
  94. _RSSwizzleClassMethod(classToSwizzle, \
  95. selector, \
  96. RSSWReturnType, \
  97. _RSSWWrapArg(RSSWArguments), \
  98. _RSSWWrapArg(RSSWReplacement))
  99. #pragma mark - Main API
  100. /**
  101. A function pointer to the original implementation of the swizzled method.
  102. */
  103. typedef void (*RSSwizzleOriginalIMP)(void /* id, SEL, ... */ );
  104. /**
  105. RSSwizzleInfo is used in the new implementation block to get and call original implementation of the swizzled method.
  106. */
  107. @interface RSSwizzleInfo : NSObject
  108. /**
  109. Returns the original implementation of the swizzled method.
  110. It is actually either an original implementation if the swizzled class implements the method itself; or a super implementation fetched from one of the superclasses.
  111. @note You must always cast returned implementation to the appropriate function pointer when calling.
  112. @return A function pointer to the original implementation of the swizzled method.
  113. */
  114. -(RSSwizzleOriginalIMP)getOriginalImplementation;
  115. /// The selector of the swizzled method.
  116. @property (nonatomic, readonly) SEL selector;
  117. @end
  118. /**
  119. A factory block returning the block for the new implementation of the swizzled method.
  120. You must always obtain original implementation with swizzleInfo and call it from the new implementation.
  121. @param swizzleInfo An info used to get and call the original implementation of the swizzled method.
  122. @return A block that implements a method.
  123. Its signature should be: `method_return_type ^(id self, method_args...)`.
  124. The selector is not available as a parameter to this block.
  125. */
  126. typedef id (^RSSwizzleImpFactoryBlock)(RSSwizzleInfo *swizzleInfo);
  127. typedef NS_ENUM(NSUInteger, RSSwizzleMode) {
  128. /// RSSwizzle always does swizzling.
  129. RSSwizzleModeAlways = 0,
  130. /// RSSwizzle does not do swizzling if the same class has been swizzled earlier with the same key.
  131. RSSwizzleModeOncePerClass = 1,
  132. /// RSSwizzle does not do swizzling if the same class or one of its superclasses have been swizzled earlier with the same key.
  133. /// @note There is no guarantee that your implementation will be called only once per method call. If the order of swizzling is: first inherited class, second superclass, then both swizzlings will be done and the new implementation will be called twice.
  134. RSSwizzleModeOncePerClassAndSuperclasses = 2
  135. };
  136. @interface RSSwizzle : NSObject
  137. #pragma mark └ Swizzle Instance Method
  138. /**
  139. Swizzles the instance method of the class with the new implementation.
  140. Original implementation must always be called from the new implementation. And because of the the fact that for safe and robust swizzling original implementation must be dynamically fetched at the time of calling and not at the time of swizzling, swizzling API is a little bit complicated.
  141. You should pass a factory block that returns the block for the new implementation of the swizzled method. And use swizzleInfo argument to retrieve and call original implementation.
  142. Example for swizzling `-(int)calculate:(int)number;` method:
  143. @code
  144. SEL selector = @selector(calculate:);
  145. [RSSwizzle
  146. swizzleInstanceMethod:selector
  147. inClass:classToSwizzle
  148. newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
  149. // This block will be used as the new implementation.
  150. return ^int(__unsafe_unretained id self, int num){
  151. // You MUST always cast implementation to the correct function pointer.
  152. int (*originalIMP)(__unsafe_unretained id, SEL, int);
  153. originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
  154. // Calling original implementation.
  155. int res = originalIMP(self,selector,num);
  156. // Returning modified return value.
  157. return res + 1;
  158. };
  159. }
  160. mode:RSSwizzleModeAlways
  161. key:NULL];
  162. @endcode
  163. Swizzling frequently goes along with checking whether this particular class (or one of its superclasses) has been already swizzled. Here the `mode` and `key` parameters can help.
  164. Here is an example of swizzling `-(void)dealloc;` only in case when neither class and no one of its superclasses has been already swizzled with our key. However "Deallocating ..." message still may be logged multiple times per method call if swizzling was called primarily for an inherited class and later for one of its superclasses.
  165. @code
  166. static const void *key = &key;
  167. SEL selector = NSSelectorFromString(@"dealloc");
  168. [RSSwizzle
  169. swizzleInstanceMethod:selector
  170. inClass:classToSwizzle
  171. newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
  172. return ^void(__unsafe_unretained id self){
  173. NSLog(@"Deallocating %@.",self);
  174. void (*originalIMP)(__unsafe_unretained id, SEL);
  175. originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
  176. originalIMP(self,selector);
  177. };
  178. }
  179. mode:RSSwizzleModeOncePerClassAndSuperclasses
  180. key:key];
  181. @endcode
  182. Swizzling is fully thread-safe.
  183. @param selector Selector of the method that should be swizzled.
  184. @param classToSwizzle The class with the method that should be swizzled.
  185. @param factoryBlock The factory block returning the block for the new implementation of the swizzled method.
  186. @param mode The mode is used in combination with the key to indicate whether the swizzling should be done for the given class.
  187. @param key The key is used in combination with the mode to indicate whether the swizzling should be done for the given class. May be NULL if the mode is RSSwizzleModeAlways.
  188. @return YES if successfully swizzled and NO if swizzling has been already done for given key and class (or one of superclasses, depends on the mode).
  189. */
  190. +(BOOL)swizzleInstanceMethod:(SEL)selector
  191. inClass:(Class)classToSwizzle
  192. newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock
  193. mode:(RSSwizzleMode)mode
  194. key:(const void *)key;
  195. #pragma mark └ Swizzle Class method
  196. /**
  197. Swizzles the class method of the class with the new implementation.
  198. Original implementation must always be called from the new implementation. And because of the the fact that for safe and robust swizzling original implementation must be dynamically fetched at the time of calling and not at the time of swizzling, swizzling API is a little bit complicated.
  199. You should pass a factory block that returns the block for the new implementation of the swizzled method. And use swizzleInfo argument to retrieve and call original implementation.
  200. Example for swizzling `+(int)calculate:(int)number;` method:
  201. @code
  202. SEL selector = @selector(calculate:);
  203. [RSSwizzle
  204. swizzleClassMethod:selector
  205. inClass:classToSwizzle
  206. newImpFactory:^id(RSSWizzleInfo *swizzleInfo) {
  207. // This block will be used as the new implementation.
  208. return ^int(__unsafe_unretained id self, int num){
  209. // You MUST always cast implementation to the correct function pointer.
  210. int (*originalIMP)(__unsafe_unretained id, SEL, int);
  211. originalIMP = (__typeof(originalIMP))[swizzleInfo getOriginalImplementation];
  212. // Calling original implementation.
  213. int res = originalIMP(self,selector,num);
  214. // Returning modified return value.
  215. return res + 1;
  216. };
  217. }];
  218. @endcode
  219. Swizzling is fully thread-safe.
  220. @param selector Selector of the method that should be swizzled.
  221. @param classToSwizzle The class with the method that should be swizzled.
  222. @param factoryBlock The factory block returning the block for the new implementation of the swizzled method.
  223. */
  224. +(void)swizzleClassMethod:(SEL)selector
  225. inClass:(Class)classToSwizzle
  226. newImpFactory:(RSSwizzleImpFactoryBlock)factoryBlock;
  227. @end
  228. #pragma mark - Implementation details
  229. // Do not write code that depends on anything below this line.
  230. // Wrapping arguments to pass them as a single argument to another macro.
  231. #define _RSSWWrapArg(args...) args
  232. #define _RSSWDel2Arg(a1, a2, args...) a1, ##args
  233. #define _RSSWDel3Arg(a1, a2, a3, args...) a1, a2, ##args
  234. // To prevent comma issues if there are no arguments we add one dummy argument
  235. // and remove it later.
  236. #define _RSSWArguments(arguments...) DEL, ##arguments
  237. #define _RSSwizzleInstanceMethod(classToSwizzle, \
  238. selector, \
  239. RSSWReturnType, \
  240. RSSWArguments, \
  241. RSSWReplacement, \
  242. RSSwizzleMode, \
  243. KEY) \
  244. [RSSwizzle \
  245. swizzleInstanceMethod:selector \
  246. inClass:[classToSwizzle class] \
  247. newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { \
  248. RSSWReturnType (*originalImplementation_)(_RSSWDel3Arg(__unsafe_unretained id, \
  249. SEL, \
  250. RSSWArguments)); \
  251. SEL selector_ = selector; \
  252. return ^RSSWReturnType (_RSSWDel2Arg(__unsafe_unretained id self, \
  253. RSSWArguments)) \
  254. { \
  255. RSSWReplacement \
  256. }; \
  257. } \
  258. mode:RSSwizzleMode \
  259. key:KEY];
  260. #define _RSSwizzleClassMethod(classToSwizzle, \
  261. selector, \
  262. RSSWReturnType, \
  263. RSSWArguments, \
  264. RSSWReplacement) \
  265. [RSSwizzle \
  266. swizzleClassMethod:selector \
  267. inClass:[classToSwizzle class] \
  268. newImpFactory:^id(RSSwizzleInfo *swizzleInfo) { \
  269. RSSWReturnType (*originalImplementation_)(_RSSWDel3Arg(__unsafe_unretained id, \
  270. SEL, \
  271. RSSWArguments)); \
  272. SEL selector_ = selector; \
  273. return ^RSSWReturnType (_RSSWDel2Arg(__unsafe_unretained id self, \
  274. RSSWArguments)) \
  275. { \
  276. RSSWReplacement \
  277. }; \
  278. }];
  279. #define _RSSWCallOriginal(arguments...) \
  280. ((__typeof(originalImplementation_))[swizzleInfo \
  281. getOriginalImplementation])(self, \
  282. selector_, \
  283. ##arguments)