├── .gitignore ├── JGMethodSwizzler.podspec ├── JGMethodSwizzler ├── JGMethodSwizzler.h └── JGMethodSwizzler.m ├── JGMethodSwizzlerTests-OSX ├── JGMethodSwizzlerTests-OSX-Info.plist ├── JGMethodSwizzlerTests-OSX-Prefix.pch └── en.lproj │ └── InfoPlist.strings ├── JGMethodSwizzlerTests.xcodeproj └── project.pbxproj ├── JGMethodSwizzlerTests ├── JGMethodSwizzlerTests-Info.plist ├── JGMethodSwizzlerTests-Prefix.pch ├── JGMethodSwizzlerTests.m └── en.lproj │ └── InfoPlist.strings ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | -------------------------------------------------------------------------------- /JGMethodSwizzler.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "JGMethodSwizzler" 4 | s.version = "2.0.1" 5 | s.summary = "Powerful and easy to use Objective-C swizzling API." 6 | s.description = <<-DESC 7 | An easy to use Objective-C API for swizzling class and instance methods, as well as swizzling instance methods on specific instances only. 8 | DESC 9 | s.homepage = "https://github.com/JonasGessner/JGMethodSwizzler" 10 | s.license = { :type => "MIT", :file => "LICENSE.txt" } 11 | s.author = "Jonas Gessner" 12 | s.social_media_url = "http://twitter.com/JonasGessner" 13 | s.ios.deployment_target = '5.0' 14 | s.osx.deployment_target = '10.8' 15 | s.source = { :git => "https://github.com/JonasGessner/JGMethodSwizzler.git", :tag => "v2.0.1" } 16 | s.source_files = "JGMethodSwizzler/*.{h,m}" 17 | s.frameworks = "Foundation" 18 | s.requires_arc = false 19 | 20 | end 21 | -------------------------------------------------------------------------------- /JGMethodSwizzler/JGMethodSwizzler.h: -------------------------------------------------------------------------------- 1 | // 2 | // JGMethodSwizzler.h 3 | // JGMethodSwizzler 4 | // 5 | // Created by Jonas Gessner 22.08.2013 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | 13 | //------------- 14 | /* typedefs */ 15 | //------------- 16 | 17 | typedef void *(* JG_IMP)(__unsafe_unretained id, SEL, ...); 18 | 19 | typedef id (^JGMethodReplacementProvider)(JG_IMP original, __unsafe_unretained Class swizzledClass, SEL selector); 20 | 21 | 22 | 23 | 24 | 25 | //---------------- 26 | /* Deswizzling */ 27 | //---------------- 28 | 29 | /** 30 | Deswizzle all methods that have been swizzled accross all instances. 31 | 32 | @return \c YES if any methods have been deswizzled successfully. 33 | */ 34 | 35 | OBJC_EXTERN BOOL deswizzleGlobal(void); 36 | 37 | 38 | /** 39 | Deswizzle all methods that have been swizzled for specific instances. 40 | 41 | @return \c YES if any methods have been deswizzled successfully. 42 | */ 43 | 44 | OBJC_EXTERN BOOL deswizzleInstances(void); 45 | 46 | 47 | /** 48 | Deswizzle all methods. 49 | 50 | @return \c YES if any methods have been deswizzled successfully. 51 | */ 52 | 53 | OBJC_EXTERN BOOL deswizzleAll(void); 54 | 55 | 56 | //----------------- 57 | /* Helper macros */ 58 | //----------------- 59 | 60 | #define JGMethodReplacement(returntype, selftype, ...) ^ returntype (__unsafe_unretained selftype self, ##__VA_ARGS__) 61 | #define JGMethodReplacementProviderBlock ^ id (JG_IMP original, __unsafe_unretained Class swizzledClass, SEL _cmd) 62 | #define JGOriginalImplementation(type, ...) ((__typeof(type (*)(__typeof(self), SEL, ...)))original)(self, _cmd, ##__VA_ARGS__) 63 | 64 | 65 | 66 | //--------------------------------------- 67 | /** @name Super easy method swizzling */ 68 | //--------------------------------------- 69 | 70 | @interface NSObject (JGMethodSwizzler) 71 | 72 | 73 | /** 74 | Swizzle the specified class method. 75 | 76 | @param selector Selector of the method to swizzle. 77 | @param replacement The replacement block to use for swizzling the method. Its signature needs to be: return_type ^(id self, ...). 78 | 79 | */ 80 | 81 | + (void)swizzleClassMethod:(SEL)selector withReplacement:(JGMethodReplacementProvider)replacementProvider; 82 | 83 | 84 | /** 85 | Swizzle the specified instance method. 86 | 87 | @param selector Selector of the method to swizzle. 88 | @param replacement The replacement block to use for swizzling the method. Its signature needs to be: return_type ^(id self, ...). 89 | 90 | */ 91 | 92 | + (void)swizzleInstanceMethod:(SEL)selector withReplacement:(JGMethodReplacementProvider)replacementProvider; 93 | 94 | @end 95 | 96 | 97 | 98 | 99 | //--------------------------------------- 100 | /** @name Super easy method swizzling */ 101 | //--------------------------------------- 102 | 103 | @interface NSObject (JGMethodDeSwizzler) 104 | 105 | /** 106 | Restore the specified class method by removing all swizzles. 107 | 108 | @param selector Selector of the swizzled method. 109 | 110 | @return \c YES if the method was successfully restored, \c NO if the method has never been swizzled. 111 | 112 | */ 113 | 114 | + (BOOL)deswizzleClassMethod:(SEL)selector; 115 | 116 | 117 | 118 | 119 | /** 120 | Restore the specified class method by removing all swizzles. 121 | 122 | @param selector Selector of the swizzled method. 123 | 124 | @return \c YES if the method was successfully restored, \c NO if the method has never been swizzled. 125 | 126 | */ 127 | 128 | + (BOOL)deswizzleInstanceMethod:(SEL)selector; 129 | 130 | 131 | 132 | 133 | 134 | /** 135 | Restore all swizzled class methods. 136 | 137 | @return \c YES if the method was successfully restored, \c NO if no method has never been swizzled 138 | 139 | */ 140 | 141 | + (BOOL)deswizzleAllClassMethods; 142 | 143 | 144 | 145 | /** 146 | Restore all swizzled instance methods. 147 | 148 | @return \c YES if the method was successfully restored, \c NO if no method has never been swizzled. 149 | 150 | */ 151 | 152 | + (BOOL)deswizzleAllInstanceMethods; 153 | 154 | 155 | 156 | 157 | /** 158 | Restore all swizzled class and instance methods. 159 | 160 | @return \c YES if the method was successfully restored, \c NO if no method has never been swizzled. 161 | 162 | */ 163 | 164 | + (BOOL)deswizzleAllMethods; 165 | 166 | @end 167 | 168 | 169 | 170 | 171 | 172 | 173 | //------------------------------------------------------------- 174 | /** @name Super easy method swizzling on specific instances */ 175 | //------------------------------------------------------------- 176 | 177 | 178 | @interface NSObject (JGInstanceSwizzler) 179 | 180 | /** 181 | Swizzle the specified instance method on this specific instance only. 182 | 183 | @param selector Selector of the method to swizzle. 184 | @param replacement The replacement block to use for swizzling the method. Its signature needs to be: return_type ^(id self, ...). 185 | 186 | */ 187 | - (void)swizzleMethod:(SEL)selector withReplacement:(JGMethodReplacementProvider)replacementProvider; 188 | 189 | 190 | 191 | /** 192 | Restore the specified method by removing all swizzles. 193 | 194 | @param selector Selector of the swizzled method. 195 | 196 | @return \c YES if the method was successfully restored, \c NO if the method has never been swizzled. 197 | 198 | */ 199 | 200 | - (BOOL)deswizzleMethod:(SEL)selector; 201 | 202 | 203 | 204 | /** 205 | Restore all swizzled methods. 206 | 207 | @return \c YES if the method was successfully restored, \c NO if no method has never been swizzled. 208 | 209 | */ 210 | 211 | - (BOOL)deswizzle; 212 | 213 | 214 | @end 215 | 216 | 217 | -------------------------------------------------------------------------------- /JGMethodSwizzler/JGMethodSwizzler.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGMethodSwizzler.m 3 | // JGMethodSwizzler 4 | // 5 | // Created by Jonas Gessner 22.08.2013 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import "JGMethodSwizzler.h" 10 | 11 | #import 12 | #import 13 | 14 | 15 | #pragma mark Defines 16 | 17 | #ifdef __clang__ 18 | #if __has_feature(objc_arc) 19 | #define JG_ARC_ENABLED 20 | #endif 21 | #endif 22 | 23 | #ifdef JG_ARC_ENABLED 24 | #define JGBridgeCast(type, obj) ((__bridge type)obj) 25 | #define releaseIfNecessary(object) 26 | #else 27 | #define JGBridgeCast(type, obj) ((type)obj) 28 | #define releaseIfNecessary(object) [object release]; 29 | #endif 30 | 31 | 32 | #define kClassKey @"k" 33 | #define kCountKey @"c" 34 | #define kIMPKey @"i" 35 | 36 | // See http://clang.llvm.org/docs/Block-ABI-Apple.html#high-level 37 | struct Block_literal_1 { 38 | void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock 39 | int flags; 40 | int reserved; 41 | void (*invoke)(void *, ...); 42 | struct Block_descriptor_1 { 43 | unsigned long int reserved; // NULL 44 | unsigned long int size; // sizeof(struct Block_literal_1) 45 | // optional helper functions 46 | void (*copy_helper)(void *dst, void *src); // IFF (1<<25) 47 | void (*dispose_helper)(void *src); // IFF (1<<25) 48 | // required ABI.2010.3.16 49 | const char *signature; // IFF (1<<30) 50 | } *descriptor; 51 | // imported variables 52 | }; 53 | 54 | enum { 55 | BLOCK_HAS_COPY_DISPOSE = (1 << 25), 56 | BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code 57 | BLOCK_IS_GLOBAL = (1 << 28), 58 | BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE 59 | BLOCK_HAS_SIGNATURE = (1 << 30), 60 | }; 61 | typedef int BlockFlags; 62 | 63 | 64 | 65 | #pragma mark - Block Analysis 66 | 67 | NS_INLINE const char *blockGetType(id block) { 68 | struct Block_literal_1 *blockRef = JGBridgeCast(struct Block_literal_1 *, block); 69 | BlockFlags flags = blockRef->flags; 70 | 71 | if (flags & BLOCK_HAS_SIGNATURE) { 72 | void *signatureLocation = blockRef->descriptor; 73 | signatureLocation += sizeof(unsigned long int); 74 | signatureLocation += sizeof(unsigned long int); 75 | 76 | if (flags & BLOCK_HAS_COPY_DISPOSE) { 77 | signatureLocation += sizeof(void(*)(void *dst, void *src)); 78 | signatureLocation += sizeof(void (*)(void *src)); 79 | } 80 | 81 | const char *signature = (*(const char **)signatureLocation); 82 | return signature; 83 | } 84 | 85 | return NULL; 86 | } 87 | 88 | NS_INLINE BOOL blockIsCompatibleWithMethodType(id block, __unsafe_unretained Class class, SEL selector, BOOL instanceMethod) { 89 | const char *blockType = blockGetType(block); 90 | 91 | NSMethodSignature *blockSignature = [NSMethodSignature signatureWithObjCTypes:blockType]; 92 | NSMethodSignature *methodSignature = (instanceMethod ? [class instanceMethodSignatureForSelector:selector] : [class methodSignatureForSelector:selector]); 93 | 94 | if (!blockSignature || !methodSignature) { 95 | return NO; 96 | } 97 | 98 | if (blockSignature.numberOfArguments != methodSignature.numberOfArguments) { 99 | return NO; 100 | } 101 | const char *blockReturnType = blockSignature.methodReturnType; 102 | 103 | if (strncmp(blockReturnType, "@", 1) == 0) { 104 | blockReturnType = "@"; 105 | } 106 | 107 | if (strcmp(blockReturnType, methodSignature.methodReturnType) != 0) { 108 | return NO; 109 | } 110 | 111 | for (unsigned int i = 0; i < methodSignature.numberOfArguments; i++) { 112 | if (i == 0) { 113 | // self in method, block in block 114 | if (strcmp([methodSignature getArgumentTypeAtIndex:i], "@") != 0) { 115 | return NO; 116 | } 117 | if (strcmp([blockSignature getArgumentTypeAtIndex:i], "@?") != 0) { 118 | return NO; 119 | } 120 | } 121 | else if(i == 1) { 122 | // SEL in method, self in block 123 | if (strcmp([methodSignature getArgumentTypeAtIndex:i], ":") != 0) { 124 | return NO; 125 | } 126 | if (instanceMethod ? strncmp([blockSignature getArgumentTypeAtIndex:i], "@", 1) != 0 : (strncmp([blockSignature getArgumentTypeAtIndex:i], "@", 1) != 0 && strcmp([blockSignature getArgumentTypeAtIndex:i], "r^#") != 0)) { 127 | return NO; 128 | } 129 | } 130 | else { 131 | const char *blockSignatureArg = [blockSignature getArgumentTypeAtIndex:i]; 132 | 133 | if (strncmp(blockSignatureArg, "@", 1) == 0) { 134 | blockSignatureArg = "@"; 135 | } 136 | 137 | if (strcmp(blockSignatureArg, [methodSignature getArgumentTypeAtIndex:i]) != 0) { 138 | return NO; 139 | } 140 | } 141 | } 142 | 143 | return YES; 144 | } 145 | 146 | NS_INLINE BOOL blockIsValidReplacementProvider(id block) { 147 | const char *blockType = blockGetType(block); 148 | 149 | JGMethodReplacementProvider dummy = JGMethodReplacementProviderBlock { 150 | return nil; 151 | }; 152 | 153 | const char *expectedType = blockGetType(dummy); 154 | 155 | return (strcmp(expectedType, blockType) == 0); 156 | } 157 | 158 | 159 | 160 | NS_INLINE void classSwizzleMethod(Class cls, Method method, IMP newImp) { 161 | if (!class_addMethod(cls, method_getName(method), newImp, method_getTypeEncoding(method))) { 162 | // class already has implementation, swizzle it instead 163 | method_setImplementation(method, newImp); 164 | } 165 | } 166 | 167 | 168 | 169 | 170 | 171 | #pragma mark - Original Implementations 172 | 173 | static OSSpinLock lock = OS_SPINLOCK_INIT; 174 | 175 | static NSMutableDictionary *originalClassMethods; 176 | static NSMutableDictionary *originalInstanceMethods; 177 | static NSMutableDictionary *originalInstanceInstanceMethods; 178 | 179 | NS_INLINE JG_IMP originalClassMethodImplementation(__unsafe_unretained Class class, SEL selector, BOOL fetchOnly) { 180 | NSCAssert(!OSSpinLockTry(&lock), @"Spin lock is not locked"); 181 | 182 | if (!originalClassMethods) { 183 | originalClassMethods = [[NSMutableDictionary alloc] init]; 184 | } 185 | 186 | NSString *classKey = NSStringFromClass(class); 187 | NSString *selectorKey = NSStringFromSelector(selector); 188 | 189 | NSMutableDictionary *classSwizzles = originalClassMethods[classKey]; 190 | 191 | NSValue *pointerValue = classSwizzles[selectorKey]; 192 | 193 | if (!classSwizzles) { 194 | classSwizzles = [NSMutableDictionary dictionary]; 195 | 196 | originalClassMethods[classKey] = classSwizzles; 197 | } 198 | 199 | JG_IMP orig = NULL; 200 | 201 | if (pointerValue) { 202 | orig = [pointerValue pointerValue]; 203 | 204 | if (fetchOnly) { 205 | if (classSwizzles.count == 1) { 206 | [originalClassMethods removeObjectForKey:classKey]; 207 | } 208 | else { 209 | [classSwizzles removeObjectForKey:selectorKey]; 210 | } 211 | } 212 | } 213 | else if (!fetchOnly) { 214 | orig = (JG_IMP)[class methodForSelector:selector]; 215 | 216 | classSwizzles[selectorKey] = [NSValue valueWithPointer:orig]; 217 | } 218 | 219 | if (classSwizzles.count == 0) { 220 | [originalClassMethods removeObjectForKey:classKey]; 221 | } 222 | 223 | if (originalClassMethods.count == 0) { 224 | releaseIfNecessary(originalClassMethods); 225 | originalClassMethods = nil; 226 | } 227 | 228 | return orig; 229 | } 230 | 231 | 232 | 233 | 234 | NS_INLINE JG_IMP originalInstanceMethodImplementation(__unsafe_unretained Class class, SEL selector, BOOL fetchOnly) { 235 | NSCAssert(!OSSpinLockTry(&lock), @"Spin lock is not locked"); 236 | 237 | if (!originalInstanceMethods) { 238 | originalInstanceMethods = [[NSMutableDictionary alloc] init]; 239 | } 240 | 241 | NSString *classKey = NSStringFromClass(class); 242 | NSString *selectorKey = NSStringFromSelector(selector); 243 | 244 | NSMutableDictionary *classSwizzles = originalInstanceMethods[classKey]; 245 | 246 | NSValue *pointerValue = classSwizzles[selectorKey]; 247 | 248 | if (!classSwizzles) { 249 | classSwizzles = [NSMutableDictionary dictionary]; 250 | 251 | originalInstanceMethods[classKey] = classSwizzles; 252 | } 253 | 254 | JG_IMP orig = NULL; 255 | 256 | if (pointerValue) { 257 | orig = [pointerValue pointerValue]; 258 | 259 | if (fetchOnly) { 260 | [classSwizzles removeObjectForKey:selectorKey]; 261 | if (classSwizzles.count == 0) { 262 | [originalInstanceMethods removeObjectForKey:classKey]; 263 | } 264 | } 265 | } 266 | else if (!fetchOnly) { 267 | orig = (JG_IMP)[class instanceMethodForSelector:selector]; 268 | 269 | classSwizzles[selectorKey] = [NSValue valueWithPointer:orig]; 270 | } 271 | 272 | if (classSwizzles.count == 0) { 273 | [originalInstanceMethods removeObjectForKey:classKey]; 274 | } 275 | 276 | if (originalInstanceMethods.count == 0) { 277 | releaseIfNecessary(originalInstanceMethods); 278 | originalInstanceMethods = nil; 279 | } 280 | 281 | return orig; 282 | } 283 | 284 | 285 | 286 | 287 | 288 | NS_INLINE JG_IMP originalInstanceInstanceMethodImplementation(__unsafe_unretained Class class, SEL selector, BOOL fetchOnly) { 289 | NSCAssert(!OSSpinLockTry(&lock), @"Spin lock is not locked"); 290 | 291 | if (!originalInstanceInstanceMethods) { 292 | originalInstanceInstanceMethods = [[NSMutableDictionary alloc] init]; 293 | } 294 | 295 | NSString *classKey = NSStringFromClass(class); 296 | NSString *selectorKey = NSStringFromSelector(selector); 297 | 298 | NSMutableDictionary *instanceSwizzles = originalInstanceInstanceMethods[classKey]; 299 | 300 | if (!instanceSwizzles) { 301 | instanceSwizzles = [NSMutableDictionary dictionary]; 302 | 303 | originalInstanceInstanceMethods[classKey] = instanceSwizzles; 304 | } 305 | 306 | JG_IMP orig = NULL; 307 | 308 | if (fetchOnly) { 309 | NSMutableDictionary *dict = instanceSwizzles[selectorKey]; 310 | if (!dict) { 311 | return NULL; 312 | } 313 | NSValue *pointerValue = dict[kIMPKey]; 314 | orig = [pointerValue pointerValue]; 315 | unsigned int count = [dict[kCountKey] unsignedIntValue]; 316 | if (count == 1) { 317 | [instanceSwizzles removeObjectForKey:selectorKey]; 318 | if (instanceSwizzles.count == 0) { 319 | [originalInstanceInstanceMethods removeObjectForKey:classKey]; 320 | } 321 | } 322 | else { 323 | dict[kCountKey] = @(count-1); 324 | } 325 | } 326 | else { 327 | NSMutableDictionary *dict = instanceSwizzles[selectorKey]; 328 | if (!dict) { 329 | dict = [NSMutableDictionary dictionaryWithCapacity:2]; 330 | dict[kCountKey] = @(1); 331 | 332 | orig = (JG_IMP)[class instanceMethodForSelector:selector]; 333 | dict[kIMPKey] = [NSValue valueWithPointer:orig]; 334 | 335 | instanceSwizzles[selectorKey] = dict; 336 | } 337 | else { 338 | orig = [dict[kIMPKey] pointerValue]; 339 | 340 | unsigned int count = [dict[kCountKey] unsignedIntValue]; 341 | dict[kCountKey] = @(count+1); 342 | } 343 | } 344 | 345 | if (originalInstanceInstanceMethods.count == 0) { 346 | releaseIfNecessary(originalInstanceInstanceMethods); 347 | originalInstanceInstanceMethods = nil; 348 | } 349 | 350 | return orig; 351 | } 352 | 353 | 354 | #pragma mark - Deswizzling Global Swizzles 355 | 356 | 357 | NS_INLINE BOOL deswizzleClassMethod(__unsafe_unretained Class class, SEL selector) { 358 | OSSpinLockLock(&lock); 359 | 360 | JG_IMP originalIMP = originalClassMethodImplementation(class, selector, YES); 361 | 362 | if (originalIMP) { 363 | method_setImplementation(class_getClassMethod(class, selector), (IMP)originalIMP); 364 | OSSpinLockUnlock(&lock); 365 | return YES; 366 | } 367 | else { 368 | OSSpinLockUnlock(&lock); 369 | return NO; 370 | } 371 | } 372 | 373 | 374 | NS_INLINE BOOL deswizzleInstanceMethod(__unsafe_unretained Class class, SEL selector) { 375 | OSSpinLockLock(&lock); 376 | 377 | JG_IMP originalIMP = originalInstanceMethodImplementation(class, selector, YES); 378 | 379 | if (originalIMP) { 380 | method_setImplementation(class_getInstanceMethod(class, selector), (IMP)originalIMP); 381 | OSSpinLockUnlock(&lock); 382 | return YES; 383 | } 384 | else { 385 | OSSpinLockUnlock(&lock); 386 | return NO; 387 | } 388 | } 389 | 390 | 391 | NS_INLINE BOOL deswizzleAllClassMethods(__unsafe_unretained Class class) { 392 | OSSpinLockLock(&lock); 393 | BOOL success = NO; 394 | NSDictionary *d = [originalClassMethods[NSStringFromClass(class)] copy]; 395 | for (NSString *sel in d) { 396 | OSSpinLockUnlock(&lock); 397 | if (deswizzleClassMethod(class, NSSelectorFromString(sel))) { 398 | success = YES; 399 | } 400 | OSSpinLockLock(&lock); 401 | } 402 | OSSpinLockUnlock(&lock); 403 | releaseIfNecessary(d); 404 | return success; 405 | } 406 | 407 | 408 | NS_INLINE BOOL deswizzleAllInstanceMethods(__unsafe_unretained Class class) { 409 | OSSpinLockLock(&lock); 410 | BOOL success = NO; 411 | NSDictionary *d = [originalInstanceMethods[NSStringFromClass(class)] copy]; 412 | for (NSString *sel in d) { 413 | OSSpinLockUnlock(&lock); 414 | if (deswizzleInstanceMethod(class, NSSelectorFromString(sel))) { 415 | success = YES; 416 | } 417 | OSSpinLockLock(&lock); 418 | } 419 | OSSpinLockUnlock(&lock); 420 | releaseIfNecessary(d); 421 | return success; 422 | } 423 | 424 | 425 | #pragma mark - Global Swizzling 426 | 427 | NS_INLINE void swizzleClassMethod(__unsafe_unretained Class class, SEL selector, JGMethodReplacementProvider replacement) { 428 | NSCAssert(blockIsValidReplacementProvider(replacement), @"Invalid method replacemt provider"); 429 | 430 | NSCAssert([class respondsToSelector:selector], @"Invalid method: +[%@ %@]", NSStringFromClass(class), NSStringFromSelector(selector)); 431 | 432 | OSSpinLockLock(&lock); 433 | 434 | Method originalMethod = class_getClassMethod(class, selector); 435 | 436 | JG_IMP orig = originalClassMethodImplementation(class, selector, NO); 437 | 438 | id replaceBlock = replacement(orig, class, selector); 439 | 440 | NSCAssert(blockIsCompatibleWithMethodType(replaceBlock, class, selector, NO), @"Invalid method replacement"); 441 | 442 | Class meta = object_getClass(class); 443 | 444 | classSwizzleMethod(meta, originalMethod, imp_implementationWithBlock(replaceBlock)); 445 | 446 | OSSpinLockUnlock(&lock); 447 | } 448 | 449 | 450 | NS_INLINE void swizzleInstanceMethod(__unsafe_unretained Class class, SEL selector, JGMethodReplacementProvider replacement) { 451 | NSCAssert(blockIsValidReplacementProvider(replacement), @"Invalid method replacemt provider"); 452 | 453 | NSCAssert([class instancesRespondToSelector:selector], @"Invalid method: -[%@ %@]", NSStringFromClass(class), NSStringFromSelector(selector)); 454 | 455 | NSCAssert(originalInstanceInstanceMethods[NSStringFromClass(class)][NSStringFromSelector(selector)] == nil, @"Swizzling an instance method that has already been swizzled on a specific instance is not supported"); 456 | 457 | OSSpinLockLock(&lock); 458 | 459 | Method originalMethod = class_getInstanceMethod(class, selector); 460 | 461 | JG_IMP orig = originalInstanceMethodImplementation(class, selector, NO); 462 | 463 | id replaceBlock = replacement(orig, class, selector); 464 | 465 | NSCAssert(blockIsCompatibleWithMethodType(replaceBlock, class, selector, YES), @"Invalid method replacement"); 466 | 467 | IMP replace = imp_implementationWithBlock(replaceBlock); 468 | 469 | classSwizzleMethod(class, originalMethod, replace); 470 | 471 | OSSpinLockUnlock(&lock); 472 | } 473 | 474 | 475 | 476 | 477 | #pragma mark - Instance Specific Swizzling & Deswizzling 478 | 479 | static NSMutableDictionary *dynamicSubclassesByObject; 480 | 481 | NS_INLINE unsigned int swizzleCount(__unsafe_unretained id object) { 482 | NSValue *key = [NSValue valueWithPointer:JGBridgeCast(const void *, object)]; 483 | 484 | unsigned int count = [dynamicSubclassesByObject[key][kCountKey] unsignedIntValue]; 485 | 486 | return count; 487 | } 488 | 489 | NS_INLINE void decreaseSwizzleCount(__unsafe_unretained id object) { 490 | NSValue *key = [NSValue valueWithPointer:JGBridgeCast(const void *, object)]; 491 | 492 | NSMutableDictionary *classDict = dynamicSubclassesByObject[key]; 493 | 494 | unsigned int count = [classDict[kCountKey] unsignedIntValue]; 495 | 496 | classDict[kCountKey] = @(count-1); 497 | } 498 | 499 | NS_INLINE BOOL deswizzleInstance(__unsafe_unretained id object) { 500 | OSSpinLockLock(&lock); 501 | 502 | BOOL success = NO; 503 | 504 | if (swizzleCount(object) > 0) { 505 | Class dynamicSubclass = object_getClass(object); 506 | 507 | object_setClass(object, [object class]); 508 | 509 | objc_disposeClassPair(dynamicSubclass); 510 | 511 | [originalInstanceInstanceMethods removeObjectForKey:NSStringFromClass([object class])]; 512 | 513 | [dynamicSubclassesByObject removeObjectForKey:[NSValue valueWithPointer:JGBridgeCast(const void *, object)]]; 514 | 515 | if (!dynamicSubclassesByObject.count) { 516 | releaseIfNecessary(dynamicSubclassesByObject); 517 | dynamicSubclassesByObject = nil; 518 | } 519 | 520 | if (!originalInstanceInstanceMethods.count) { 521 | releaseIfNecessary(originalInstanceInstanceMethods); 522 | originalInstanceInstanceMethods = nil; 523 | } 524 | 525 | success = YES; 526 | } 527 | 528 | OSSpinLockUnlock(&lock); 529 | 530 | return success; 531 | } 532 | 533 | NS_INLINE BOOL deswizzleMethod(__unsafe_unretained id object, SEL selector) { 534 | OSSpinLockLock(&lock); 535 | 536 | BOOL success = NO; 537 | 538 | unsigned int count = swizzleCount(object); 539 | 540 | if (count == 1) { 541 | OSSpinLockUnlock(&lock); 542 | return deswizzleInstance(object); 543 | } 544 | else if (count > 1) { 545 | JG_IMP originalIMP = originalInstanceInstanceMethodImplementation([object class], selector, YES); 546 | if (originalIMP) { 547 | method_setImplementation(class_getInstanceMethod(object_getClass(object), selector), (IMP)originalIMP); 548 | 549 | success = YES; 550 | } 551 | 552 | decreaseSwizzleCount(object); 553 | } 554 | 555 | OSSpinLockUnlock(&lock); 556 | 557 | return success; 558 | } 559 | 560 | 561 | NS_INLINE void swizzleInstance(__unsafe_unretained id object, SEL selector, JGMethodReplacementProvider replacementProvider) { 562 | Class class = [object class]; 563 | 564 | NSCAssert(blockIsValidReplacementProvider(replacementProvider), @"Invalid method replacemt provider"); 565 | 566 | NSCAssert([object respondsToSelector:selector], @"Invalid method: -[%@ %@]", NSStringFromClass(class), NSStringFromSelector(selector)); 567 | 568 | OSSpinLockLock(&lock); 569 | 570 | if (!dynamicSubclassesByObject) { 571 | dynamicSubclassesByObject = [[NSMutableDictionary alloc] init]; 572 | }; 573 | 574 | NSValue *key = [NSValue valueWithPointer:JGBridgeCast(const void *, object)]; 575 | 576 | NSMutableDictionary *classDict = dynamicSubclassesByObject[key]; 577 | 578 | Class newClass = [classDict[kClassKey] pointerValue]; 579 | 580 | if (!classDict || !newClass) { 581 | NSString *dynamicSubclass = [NSStringFromClass(class) stringByAppendingFormat:@"_JGMS_%@", [[NSUUID UUID] UUIDString]]; 582 | 583 | const char *newClsName = [dynamicSubclass UTF8String]; 584 | 585 | NSCAssert(!objc_lookUpClass(newClsName), @"Class %@ already exists!\n", dynamicSubclass); 586 | 587 | newClass = objc_allocateClassPair(class, newClsName, 0); 588 | 589 | NSCAssert(newClass, @"Could not create class %@\n", dynamicSubclass); 590 | 591 | objc_registerClassPair(newClass); 592 | 593 | classDict = [NSMutableDictionary dictionary]; 594 | classDict[kClassKey] = [NSValue valueWithPointer:JGBridgeCast(const void *, newClass)]; 595 | classDict[kCountKey] = @(1); 596 | 597 | dynamicSubclassesByObject[[NSValue valueWithPointer:JGBridgeCast(const void *, object)]] = classDict; 598 | 599 | Method classMethod = class_getInstanceMethod(newClass, @selector(class)); 600 | 601 | id swizzledClass = ^Class (__unsafe_unretained id self) { 602 | return class; 603 | }; 604 | 605 | classSwizzleMethod(newClass, classMethod, imp_implementationWithBlock(swizzledClass)); 606 | 607 | SEL deallocSel = sel_getUid("dealloc"); 608 | 609 | Method dealloc = class_getInstanceMethod(newClass, deallocSel); 610 | __block JG_IMP deallocImp = (JG_IMP)method_getImplementation(dealloc); 611 | 612 | id deallocHandler = ^(__unsafe_unretained id self) { 613 | NSCAssert(deswizzleInstance(self), @"Deswizzling of class %@ failed", NSStringFromClass([self class])); 614 | 615 | if (deallocImp) { 616 | deallocImp(self, deallocSel); 617 | } 618 | }; 619 | 620 | classSwizzleMethod(newClass, dealloc, imp_implementationWithBlock(deallocHandler)); 621 | } 622 | else { 623 | unsigned int count = [classDict[kCountKey] unsignedIntValue]; 624 | classDict[kCountKey] = @(count+1); 625 | } 626 | 627 | Method origMethod = class_getInstanceMethod(class, selector); 628 | 629 | JG_IMP origIMP = originalInstanceInstanceMethodImplementation([object class], selector, NO); 630 | 631 | id replaceBlock = replacementProvider(origIMP, class, selector); 632 | 633 | NSCAssert(blockIsCompatibleWithMethodType(replaceBlock, class, selector, YES), @"Invalid method replacement"); 634 | 635 | classSwizzleMethod(newClass, origMethod, imp_implementationWithBlock(replaceBlock)); 636 | 637 | object_setClass(object, newClass); 638 | 639 | OSSpinLockUnlock(&lock); 640 | } 641 | 642 | 643 | 644 | #pragma mark - Category Implementations 645 | 646 | @implementation NSObject (JGMethodSwizzler) 647 | 648 | + (void)swizzleClassMethod:(SEL)selector withReplacement:(JGMethodReplacementProvider)replacementProvider { 649 | swizzleClassMethod(self, selector, replacementProvider); 650 | } 651 | 652 | + (void)swizzleInstanceMethod:(SEL)selector withReplacement:(JGMethodReplacementProvider)replacementProvider { 653 | swizzleInstanceMethod(self, selector, replacementProvider); 654 | } 655 | 656 | @end 657 | 658 | 659 | @implementation NSObject (JGMethodDeSwizzler) 660 | 661 | + (BOOL)deswizzleClassMethod:(SEL)selector { 662 | return deswizzleClassMethod(self, selector); 663 | } 664 | 665 | + (BOOL)deswizzleInstanceMethod:(SEL)selector { 666 | return deswizzleInstanceMethod(self, selector); 667 | } 668 | 669 | + (BOOL)deswizzleAllClassMethods { 670 | return deswizzleAllClassMethods(self); 671 | } 672 | 673 | + (BOOL)deswizzleAllInstanceMethods { 674 | return deswizzleAllInstanceMethods(self); 675 | } 676 | 677 | + (BOOL)deswizzleAllMethods { 678 | BOOL c = [self deswizzleAllClassMethods]; 679 | BOOL i = [self deswizzleAllInstanceMethods]; 680 | return (c || i); 681 | } 682 | 683 | @end 684 | 685 | 686 | 687 | @implementation NSObject (JGInstanceSwizzler) 688 | 689 | - (void)swizzleMethod:(SEL)selector withReplacement:(JGMethodReplacementProvider)replacementProvider { 690 | swizzleInstance(self, selector, replacementProvider); 691 | } 692 | 693 | - (BOOL)deswizzleMethod:(SEL)selector { 694 | return deswizzleMethod(self, selector); 695 | } 696 | 697 | - (BOOL)deswizzle { 698 | return deswizzleInstance(self); 699 | } 700 | 701 | @end 702 | 703 | 704 | 705 | #pragma mark - Public functions 706 | 707 | BOOL deswizzleGlobal(void) { 708 | BOOL success = NO; 709 | OSSpinLockLock(&lock); 710 | NSDictionary *d = originalClassMethods.copy; 711 | for (NSString *classKey in d) { 712 | OSSpinLockUnlock(&lock); 713 | BOOL ok = [NSClassFromString(classKey) deswizzleAllMethods]; 714 | OSSpinLockLock(&lock); 715 | if (!success == ok) { 716 | success = YES; 717 | } 718 | } 719 | 720 | NSDictionary *d1 = originalInstanceMethods.copy; 721 | for (NSString *classKey in d1) { 722 | OSSpinLockUnlock(&lock); 723 | BOOL ok = [NSClassFromString(classKey) deswizzleAllMethods]; 724 | OSSpinLockLock(&lock); 725 | if (!success == ok) { 726 | success = YES; 727 | } 728 | } 729 | OSSpinLockUnlock(&lock); 730 | 731 | releaseIfNecessary(d); 732 | releaseIfNecessary(d1); 733 | 734 | return success; 735 | } 736 | 737 | 738 | BOOL deswizzleInstances(void) { 739 | OSSpinLockLock(&lock); 740 | BOOL success = NO; 741 | NSDictionary *d = dynamicSubclassesByObject.copy; 742 | for (NSValue *pointer in d) { 743 | id object = [pointer pointerValue]; 744 | OSSpinLockUnlock(&lock); 745 | BOOL ok = [object deswizzle]; 746 | OSSpinLockLock(&lock); 747 | if (!success && ok) { 748 | success = YES; 749 | } 750 | } 751 | OSSpinLockUnlock(&lock); 752 | 753 | releaseIfNecessary(d); 754 | 755 | return success; 756 | } 757 | 758 | BOOL deswizzleAll(void) { 759 | BOOL a = deswizzleGlobal(); 760 | BOOL b = deswizzleInstances(); 761 | 762 | return (a || b); 763 | } 764 | 765 | //For debugging purposes: 766 | //NSString *getStatus() { 767 | // return [NSString stringWithFormat:@"Original Class:\n%@\n\n\nOriginal Instance:\n%@\n\n\nOriginal Instance Specific:\n%@\n\n\nDynamic Subclasses:\n%@\n\n\n", originalClassMethods, originalInstanceMethods, originalInstanceInstanceMethods, dynamicSubclassesByObject]; 768 | //} 769 | 770 | 771 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests-OSX/JGMethodSwizzlerTests-OSX-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | de.j-gessner.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests-OSX/JGMethodSwizzlerTests-OSX-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests-OSX/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DD02B5FF181C7FC1005C543D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD02B5FE181C7FC1005C543D /* CoreGraphics.framework */; }; 11 | DD02B602181C8004005C543D /* JGMethodSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = DD02B601181C8004005C543D /* JGMethodSwizzler.m */; }; 12 | DD0E9BF0181C7E56009B37E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0E9BEF181C7E56009B37E2 /* XCTest.framework */; }; 13 | DD0E9BF1181C7E56009B37E2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDB2DD7B17C681C30077C18E /* Foundation.framework */; }; 14 | DD0E9BF3181C7E56009B37E2 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0E9BF2181C7E56009B37E2 /* UIKit.framework */; }; 15 | DD0E9BF9181C7E56009B37E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DD0E9BF7181C7E56009B37E2 /* InfoPlist.strings */; }; 16 | DD0E9BFB181C7E56009B37E2 /* JGMethodSwizzlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E9BFA181C7E56009B37E2 /* JGMethodSwizzlerTests.m */; }; 17 | DDC3227A181D62E800848CAC /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0E9BEF181C7E56009B37E2 /* XCTest.framework */; }; 18 | DDC32280181D62E800848CAC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = DDC3227E181D62E800848CAC /* InfoPlist.strings */; }; 19 | DDC3228A181D631700848CAC /* JGMethodSwizzlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = DD0E9BFA181C7E56009B37E2 /* JGMethodSwizzlerTests.m */; }; 20 | DDC3228B181D631800848CAC /* JGMethodSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = DD02B601181C8004005C543D /* JGMethodSwizzler.m */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXContainerItemProxy section */ 24 | DDC32284181D62E800848CAC /* PBXContainerItemProxy */ = { 25 | isa = PBXContainerItemProxy; 26 | containerPortal = DDB2DD7017C681C30077C18E /* Project object */; 27 | proxyType = 1; 28 | remoteGlobalIDString = DD0E9BED181C7E56009B37E2; 29 | remoteInfo = JGMethodSwizzlerTests; 30 | }; 31 | /* End PBXContainerItemProxy section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | DD02B5FE181C7FC1005C543D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 35 | DD02B600181C8004005C543D /* JGMethodSwizzler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = JGMethodSwizzler.h; path = JGMethodSwizzler/JGMethodSwizzler.h; sourceTree = SOURCE_ROOT; }; 36 | DD02B601181C8004005C543D /* JGMethodSwizzler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = JGMethodSwizzler.m; path = JGMethodSwizzler/JGMethodSwizzler.m; sourceTree = SOURCE_ROOT; }; 37 | DD0E9BEE181C7E56009B37E2 /* JGMethodSwizzlerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JGMethodSwizzlerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | DD0E9BEF181C7E56009B37E2 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 39 | DD0E9BF2181C7E56009B37E2 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 40 | DD0E9BF6181C7E56009B37E2 /* JGMethodSwizzlerTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JGMethodSwizzlerTests-Info.plist"; sourceTree = ""; }; 41 | DD0E9BF8181C7E56009B37E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 42 | DD0E9BFA181C7E56009B37E2 /* JGMethodSwizzlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = JGMethodSwizzlerTests.m; path = JGMethodSwizzlerTests/JGMethodSwizzlerTests.m; sourceTree = ""; }; 43 | DD0E9BFC181C7E56009B37E2 /* JGMethodSwizzlerTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JGMethodSwizzlerTests-Prefix.pch"; sourceTree = ""; }; 44 | DDB2DD7B17C681C30077C18E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 45 | DDB2DDAE17C6824D0077C18E /* libsubstrate.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsubstrate.dylib; path = ../../../../../opt/theos/lib/libsubstrate.dylib; sourceTree = ""; }; 46 | DDC32279181D62E800848CAC /* JGMethodSwizzlerTests-OSX.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "JGMethodSwizzlerTests-OSX.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | DDC3227D181D62E800848CAC /* JGMethodSwizzlerTests-OSX-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "JGMethodSwizzlerTests-OSX-Info.plist"; sourceTree = ""; }; 48 | DDC3227F181D62E800848CAC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 49 | DDC32283181D62E800848CAC /* JGMethodSwizzlerTests-OSX-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JGMethodSwizzlerTests-OSX-Prefix.pch"; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | DD0E9BEB181C7E56009B37E2 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | DD02B5FF181C7FC1005C543D /* CoreGraphics.framework in Frameworks */, 58 | DD0E9BF0181C7E56009B37E2 /* XCTest.framework in Frameworks */, 59 | DD0E9BF3181C7E56009B37E2 /* UIKit.framework in Frameworks */, 60 | DD0E9BF1181C7E56009B37E2 /* Foundation.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | DDC32276181D62E800848CAC /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | DDC3227A181D62E800848CAC /* XCTest.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | DD0E9BF4181C7E56009B37E2 /* JGMethodSwizzlerTests */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | DD0E9BF5181C7E56009B37E2 /* Supporting Files */, 79 | ); 80 | path = JGMethodSwizzlerTests; 81 | sourceTree = ""; 82 | }; 83 | DD0E9BF5181C7E56009B37E2 /* Supporting Files */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | DD0E9BF6181C7E56009B37E2 /* JGMethodSwizzlerTests-Info.plist */, 87 | DD0E9BF7181C7E56009B37E2 /* InfoPlist.strings */, 88 | DD0E9BFC181C7E56009B37E2 /* JGMethodSwizzlerTests-Prefix.pch */, 89 | ); 90 | name = "Supporting Files"; 91 | sourceTree = ""; 92 | }; 93 | DDB2DD6F17C681C30077C18E = { 94 | isa = PBXGroup; 95 | children = ( 96 | DDC32289181D62F300848CAC /* JGMethodSwizzler */, 97 | DDC3228C181D633F00848CAC /* Tests */, 98 | DD0E9BF4181C7E56009B37E2 /* JGMethodSwizzlerTests */, 99 | DDC3227B181D62E800848CAC /* JGMethodSwizzlerTests-OSX */, 100 | DDB2DD7A17C681C30077C18E /* Frameworks */, 101 | DDB2DD7917C681C30077C18E /* Products */, 102 | ); 103 | sourceTree = ""; 104 | }; 105 | DDB2DD7917C681C30077C18E /* Products */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | DD0E9BEE181C7E56009B37E2 /* JGMethodSwizzlerTests.xctest */, 109 | DDC32279181D62E800848CAC /* JGMethodSwizzlerTests-OSX.xctest */, 110 | ); 111 | name = Products; 112 | sourceTree = ""; 113 | }; 114 | DDB2DD7A17C681C30077C18E /* Frameworks */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | DD02B5FE181C7FC1005C543D /* CoreGraphics.framework */, 118 | DDB2DDAE17C6824D0077C18E /* libsubstrate.dylib */, 119 | DDB2DD7B17C681C30077C18E /* Foundation.framework */, 120 | DD0E9BEF181C7E56009B37E2 /* XCTest.framework */, 121 | DD0E9BF2181C7E56009B37E2 /* UIKit.framework */, 122 | ); 123 | name = Frameworks; 124 | sourceTree = ""; 125 | }; 126 | DDC3227B181D62E800848CAC /* JGMethodSwizzlerTests-OSX */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | DDC3227C181D62E800848CAC /* Supporting Files */, 130 | ); 131 | path = "JGMethodSwizzlerTests-OSX"; 132 | sourceTree = ""; 133 | }; 134 | DDC3227C181D62E800848CAC /* Supporting Files */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | DDC3227D181D62E800848CAC /* JGMethodSwizzlerTests-OSX-Info.plist */, 138 | DDC3227E181D62E800848CAC /* InfoPlist.strings */, 139 | DDC32283181D62E800848CAC /* JGMethodSwizzlerTests-OSX-Prefix.pch */, 140 | ); 141 | name = "Supporting Files"; 142 | sourceTree = ""; 143 | }; 144 | DDC32289181D62F300848CAC /* JGMethodSwizzler */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | DD02B600181C8004005C543D /* JGMethodSwizzler.h */, 148 | DD02B601181C8004005C543D /* JGMethodSwizzler.m */, 149 | ); 150 | name = JGMethodSwizzler; 151 | sourceTree = ""; 152 | }; 153 | DDC3228C181D633F00848CAC /* Tests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | DD0E9BFA181C7E56009B37E2 /* JGMethodSwizzlerTests.m */, 157 | ); 158 | name = Tests; 159 | sourceTree = ""; 160 | }; 161 | /* End PBXGroup section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | DD0E9BED181C7E56009B37E2 /* JGMethodSwizzlerTests */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = DD0E9BFD181C7E56009B37E2 /* Build configuration list for PBXNativeTarget "JGMethodSwizzlerTests" */; 167 | buildPhases = ( 168 | DD0E9BEA181C7E56009B37E2 /* Sources */, 169 | DD0E9BEB181C7E56009B37E2 /* Frameworks */, 170 | DD0E9BEC181C7E56009B37E2 /* Resources */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = JGMethodSwizzlerTests; 177 | productName = JGMethodSwizzlerTests; 178 | productReference = DD0E9BEE181C7E56009B37E2 /* JGMethodSwizzlerTests.xctest */; 179 | productType = "com.apple.product-type.bundle.unit-test"; 180 | }; 181 | DDC32278181D62E800848CAC /* JGMethodSwizzlerTests-OSX */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = DDC32286181D62E800848CAC /* Build configuration list for PBXNativeTarget "JGMethodSwizzlerTests-OSX" */; 184 | buildPhases = ( 185 | DDC32275181D62E800848CAC /* Sources */, 186 | DDC32276181D62E800848CAC /* Frameworks */, 187 | DDC32277181D62E800848CAC /* Resources */, 188 | ); 189 | buildRules = ( 190 | ); 191 | dependencies = ( 192 | DDC32285181D62E800848CAC /* PBXTargetDependency */, 193 | ); 194 | name = "JGMethodSwizzlerTests-OSX"; 195 | productName = "JGMethodSwizzlerTests-OSX"; 196 | productReference = DDC32279181D62E800848CAC /* JGMethodSwizzlerTests-OSX.xctest */; 197 | productType = "com.apple.product-type.bundle.unit-test"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | DDB2DD7017C681C30077C18E /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | LastUpgradeCheck = 0510; 206 | ORGANIZATIONNAME = "Jonas Gessner"; 207 | TargetAttributes = { 208 | DDC32278181D62E800848CAC = { 209 | TestTargetID = DD0E9BED181C7E56009B37E2; 210 | }; 211 | }; 212 | }; 213 | buildConfigurationList = DDB2DD7317C681C30077C18E /* Build configuration list for PBXProject "JGMethodSwizzlerTests" */; 214 | compatibilityVersion = "Xcode 3.2"; 215 | developmentRegion = English; 216 | hasScannedForEncodings = 0; 217 | knownRegions = ( 218 | en, 219 | ); 220 | mainGroup = DDB2DD6F17C681C30077C18E; 221 | productRefGroup = DDB2DD7917C681C30077C18E /* Products */; 222 | projectDirPath = ""; 223 | projectRoot = ""; 224 | targets = ( 225 | DD0E9BED181C7E56009B37E2 /* JGMethodSwizzlerTests */, 226 | DDC32278181D62E800848CAC /* JGMethodSwizzlerTests-OSX */, 227 | ); 228 | }; 229 | /* End PBXProject section */ 230 | 231 | /* Begin PBXResourcesBuildPhase section */ 232 | DD0E9BEC181C7E56009B37E2 /* Resources */ = { 233 | isa = PBXResourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | DD0E9BF9181C7E56009B37E2 /* InfoPlist.strings in Resources */, 237 | ); 238 | runOnlyForDeploymentPostprocessing = 0; 239 | }; 240 | DDC32277181D62E800848CAC /* Resources */ = { 241 | isa = PBXResourcesBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | DDC32280181D62E800848CAC /* InfoPlist.strings in Resources */, 245 | ); 246 | runOnlyForDeploymentPostprocessing = 0; 247 | }; 248 | /* End PBXResourcesBuildPhase section */ 249 | 250 | /* Begin PBXSourcesBuildPhase section */ 251 | DD0E9BEA181C7E56009B37E2 /* Sources */ = { 252 | isa = PBXSourcesBuildPhase; 253 | buildActionMask = 2147483647; 254 | files = ( 255 | DD0E9BFB181C7E56009B37E2 /* JGMethodSwizzlerTests.m in Sources */, 256 | DD02B602181C8004005C543D /* JGMethodSwizzler.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | DDC32275181D62E800848CAC /* Sources */ = { 261 | isa = PBXSourcesBuildPhase; 262 | buildActionMask = 2147483647; 263 | files = ( 264 | DDC3228A181D631700848CAC /* JGMethodSwizzlerTests.m in Sources */, 265 | DDC3228B181D631800848CAC /* JGMethodSwizzler.m in Sources */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | /* End PBXSourcesBuildPhase section */ 270 | 271 | /* Begin PBXTargetDependency section */ 272 | DDC32285181D62E800848CAC /* PBXTargetDependency */ = { 273 | isa = PBXTargetDependency; 274 | target = DD0E9BED181C7E56009B37E2 /* JGMethodSwizzlerTests */; 275 | targetProxy = DDC32284181D62E800848CAC /* PBXContainerItemProxy */; 276 | }; 277 | /* End PBXTargetDependency section */ 278 | 279 | /* Begin PBXVariantGroup section */ 280 | DD0E9BF7181C7E56009B37E2 /* InfoPlist.strings */ = { 281 | isa = PBXVariantGroup; 282 | children = ( 283 | DD0E9BF8181C7E56009B37E2 /* en */, 284 | ); 285 | name = InfoPlist.strings; 286 | sourceTree = ""; 287 | }; 288 | DDC3227E181D62E800848CAC /* InfoPlist.strings */ = { 289 | isa = PBXVariantGroup; 290 | children = ( 291 | DDC3227F181D62E800848CAC /* en */, 292 | ); 293 | name = InfoPlist.strings; 294 | sourceTree = ""; 295 | }; 296 | /* End PBXVariantGroup section */ 297 | 298 | /* Begin XCBuildConfiguration section */ 299 | DD0E9BFE181C7E56009B37E2 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | FRAMEWORK_SEARCH_PATHS = ( 303 | "$(SDKROOT)/Developer/Library/Frameworks", 304 | "$(inherited)", 305 | "$(DEVELOPER_FRAMEWORKS_DIR)", 306 | ); 307 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 308 | GCC_PREFIX_HEADER = "JGMethodSwizzlerTests/JGMethodSwizzlerTests-Prefix.pch"; 309 | GCC_PREPROCESSOR_DEFINITIONS = ( 310 | "DEBUG=1", 311 | "$(inherited)", 312 | ); 313 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 314 | INFOPLIST_FILE = "JGMethodSwizzlerTests/JGMethodSwizzlerTests-Info.plist"; 315 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 316 | PRODUCT_NAME = "$(TARGET_NAME)"; 317 | WRAPPER_EXTENSION = xctest; 318 | }; 319 | name = Debug; 320 | }; 321 | DD0E9BFF181C7E56009B37E2 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | FRAMEWORK_SEARCH_PATHS = ( 325 | "$(SDKROOT)/Developer/Library/Frameworks", 326 | "$(inherited)", 327 | "$(DEVELOPER_FRAMEWORKS_DIR)", 328 | ); 329 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 330 | GCC_PREFIX_HEADER = "JGMethodSwizzlerTests/JGMethodSwizzlerTests-Prefix.pch"; 331 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 332 | INFOPLIST_FILE = "JGMethodSwizzlerTests/JGMethodSwizzlerTests-Info.plist"; 333 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 334 | PRODUCT_NAME = "$(TARGET_NAME)"; 335 | WRAPPER_EXTENSION = xctest; 336 | }; 337 | name = Release; 338 | }; 339 | DDB2DDA217C681C40077C18E /* Debug */ = { 340 | isa = XCBuildConfiguration; 341 | buildSettings = { 342 | ALWAYS_SEARCH_USER_PATHS = NO; 343 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 344 | CLANG_CXX_LIBRARY = "libc++"; 345 | CLANG_ENABLE_MODULES = YES; 346 | CLANG_ENABLE_OBJC_ARC = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_CONSTANT_CONVERSION = YES; 349 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 350 | CLANG_WARN_EMPTY_BODY = YES; 351 | CLANG_WARN_ENUM_CONVERSION = YES; 352 | CLANG_WARN_INT_CONVERSION = YES; 353 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 356 | COPY_PHASE_STRIP = NO; 357 | GCC_C_LANGUAGE_STANDARD = gnu99; 358 | GCC_DYNAMIC_NO_PIC = NO; 359 | GCC_OPTIMIZATION_LEVEL = 0; 360 | GCC_PREPROCESSOR_DEFINITIONS = ( 361 | "DEBUG=1", 362 | "$(inherited)", 363 | ); 364 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 371 | ONLY_ACTIVE_ARCH = YES; 372 | SDKROOT = iphoneos; 373 | TARGETED_DEVICE_FAMILY = "1,2"; 374 | }; 375 | name = Debug; 376 | }; 377 | DDB2DDA317C681C40077C18E /* Release */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 382 | CLANG_CXX_LIBRARY = "libc++"; 383 | CLANG_ENABLE_MODULES = YES; 384 | CLANG_ENABLE_OBJC_ARC = YES; 385 | CLANG_WARN_BOOL_CONVERSION = YES; 386 | CLANG_WARN_CONSTANT_CONVERSION = YES; 387 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INT_CONVERSION = YES; 391 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 392 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 393 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 394 | COPY_PHASE_STRIP = YES; 395 | ENABLE_NS_ASSERTIONS = NO; 396 | GCC_C_LANGUAGE_STANDARD = gnu99; 397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 398 | GCC_WARN_UNDECLARED_SELECTOR = YES; 399 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 400 | GCC_WARN_UNUSED_FUNCTION = YES; 401 | GCC_WARN_UNUSED_VARIABLE = YES; 402 | IPHONEOS_DEPLOYMENT_TARGET = 4.3; 403 | SDKROOT = iphoneos; 404 | TARGETED_DEVICE_FAMILY = "1,2"; 405 | VALIDATE_PRODUCT = YES; 406 | }; 407 | name = Release; 408 | }; 409 | DDC32287181D62E800848CAC /* Debug */ = { 410 | isa = XCBuildConfiguration; 411 | buildSettings = { 412 | COMBINE_HIDPI_IMAGES = YES; 413 | FRAMEWORK_SEARCH_PATHS = ( 414 | "$(DEVELOPER_FRAMEWORKS_DIR)", 415 | "$(inherited)", 416 | ); 417 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 418 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 419 | GCC_PREFIX_HEADER = "JGMethodSwizzlerTests-OSX/JGMethodSwizzlerTests-OSX-Prefix.pch"; 420 | GCC_PREPROCESSOR_DEFINITIONS = ( 421 | "DEBUG=1", 422 | "$(inherited)", 423 | ); 424 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 425 | INFOPLIST_FILE = "JGMethodSwizzlerTests-OSX/JGMethodSwizzlerTests-OSX-Info.plist"; 426 | MACOSX_DEPLOYMENT_TARGET = 10.8; 427 | PRODUCT_NAME = "$(TARGET_NAME)"; 428 | SDKROOT = macosx; 429 | WRAPPER_EXTENSION = xctest; 430 | }; 431 | name = Debug; 432 | }; 433 | DDC32288181D62E800848CAC /* Release */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | COMBINE_HIDPI_IMAGES = YES; 437 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 438 | FRAMEWORK_SEARCH_PATHS = ( 439 | "$(DEVELOPER_FRAMEWORKS_DIR)", 440 | "$(inherited)", 441 | ); 442 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 443 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 444 | GCC_PREFIX_HEADER = "JGMethodSwizzlerTests-OSX/JGMethodSwizzlerTests-OSX-Prefix.pch"; 445 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 446 | INFOPLIST_FILE = "JGMethodSwizzlerTests-OSX/JGMethodSwizzlerTests-OSX-Info.plist"; 447 | MACOSX_DEPLOYMENT_TARGET = 10.8; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SDKROOT = macosx; 450 | WRAPPER_EXTENSION = xctest; 451 | }; 452 | name = Release; 453 | }; 454 | /* End XCBuildConfiguration section */ 455 | 456 | /* Begin XCConfigurationList section */ 457 | DD0E9BFD181C7E56009B37E2 /* Build configuration list for PBXNativeTarget "JGMethodSwizzlerTests" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | DD0E9BFE181C7E56009B37E2 /* Debug */, 461 | DD0E9BFF181C7E56009B37E2 /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | DDB2DD7317C681C30077C18E /* Build configuration list for PBXProject "JGMethodSwizzlerTests" */ = { 467 | isa = XCConfigurationList; 468 | buildConfigurations = ( 469 | DDB2DDA217C681C40077C18E /* Debug */, 470 | DDB2DDA317C681C40077C18E /* Release */, 471 | ); 472 | defaultConfigurationIsVisible = 0; 473 | defaultConfigurationName = Release; 474 | }; 475 | DDC32286181D62E800848CAC /* Build configuration list for PBXNativeTarget "JGMethodSwizzlerTests-OSX" */ = { 476 | isa = XCConfigurationList; 477 | buildConfigurations = ( 478 | DDC32287181D62E800848CAC /* Debug */, 479 | DDC32288181D62E800848CAC /* Release */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | /* End XCConfigurationList section */ 485 | }; 486 | rootObject = DDB2DD7017C681C30077C18E /* Project object */; 487 | } 488 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests/JGMethodSwizzlerTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | de.j-gessner.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests/JGMethodSwizzlerTests-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #import 10 | #endif 11 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests/JGMethodSwizzlerTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // JGMethodSwizzlerTests.m 3 | // JGMethodSwizzlerTests 4 | // 5 | // Created by Jonas Gessner on 27.10.13. 6 | // Copyright (c) 2013 Jonas Gessner. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "JGMethodSwizzler.h" 11 | 12 | 13 | @interface JGMethodSwizzlerTests : XCTestCase 14 | 15 | @end 16 | 17 | @implementation JGMethodSwizzlerTests 18 | 19 | 20 | 21 | - (int)a:(int)b { 22 | return b-2; 23 | } 24 | 25 | + (CGRect)testRect { 26 | return CGRectMake(0.0f, 1.0f, 2.0f, 3.0f); 27 | } 28 | 29 | 30 | + (CGRect)testRect2:(CGRect)r { 31 | return CGRectInset(r, 10.0f, 10.0f); 32 | } 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | - (NSObject *)applySwizzles { 41 | int add = arc4random_uniform(50); 42 | 43 | [self.class swizzleInstanceMethod:@selector(a:) withReplacement:JGMethodReplacementProviderBlock { 44 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int b) { 45 | int orig = JGOriginalImplementation(int, b); 46 | return orig+add; 47 | }; 48 | }]; 49 | 50 | int yoo = arc4random_uniform(100); 51 | 52 | int aa = [self a:yoo]; 53 | 54 | XCTAssert(aa == yoo-2+add, @"Integer calculation mismatch"); 55 | 56 | [self.class swizzleClassMethod:@selector(testRect) withReplacement:JGMethodReplacementProviderBlock { 57 | return JGMethodReplacement(CGRect, const Class *) { 58 | CGRect orig = JGOriginalImplementation(CGRect); 59 | 60 | return CGRectInset(orig, -5.0f, -5.0f); 61 | }; 62 | }]; 63 | 64 | 65 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), -5.0f, -5.0f)), @"CGRect swizzling failed"); 66 | 67 | [self.class swizzleClassMethod:@selector(testRect2:) withReplacement:JGMethodReplacementProviderBlock { 68 | return JGMethodReplacement(CGRect, const Class *, CGRect rect) { 69 | CGRect orig = JGOriginalImplementation(CGRect, rect); 70 | 71 | return CGRectInset(orig, -5.0f, -5.0f); 72 | }; 73 | }]; 74 | 75 | 76 | CGRect testRect = (CGRect){{(CGFloat)arc4random_uniform(100), (CGFloat)arc4random_uniform(100)}, {(CGFloat)arc4random_uniform(100), (CGFloat)arc4random_uniform(100)}}; 77 | 78 | XCTAssert(CGRectEqualToRect([self.class testRect2:testRect], CGRectInset(CGRectInset(testRect, 10.0f, 10.0f), -5.0f, -5.0f)), @"CGRect swizzling (2) failed"); 79 | 80 | 81 | NSObject *object = [NSObject new]; 82 | 83 | 84 | [object swizzleMethod:@selector(description) withReplacement:JGMethodReplacementProviderBlock { 85 | return JGMethodReplacement(NSString *, NSObject *) { 86 | NSString *orig = JGOriginalImplementation(NSString *); 87 | 88 | return [orig stringByAppendingString:@"Only swizzled this instance"]; 89 | }; 90 | }]; 91 | 92 | XCTAssert([[object description] hasSuffix:@"Only swizzled this instance"] && ![[[NSObject new] description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed"); 93 | 94 | [object swizzleMethod:@selector(init) withReplacement:JGMethodReplacementProviderBlock { 95 | return JGMethodReplacement(id, NSObject *) { 96 | id orig = JGOriginalImplementation(id); 97 | 98 | return orig; 99 | }; 100 | }]; 101 | 102 | return object; 103 | } 104 | 105 | - (void)removeSwizzles1:(NSObject *)object { 106 | BOOL ok = [object deswizzleMethod:@selector(description)]; 107 | BOOL ok1 = [object deswizzleMethod:@selector(init)]; 108 | BOOL ok2 = [object deswizzle]; 109 | BOOL ok3 = deswizzleInstances(); 110 | 111 | XCTAssert(ok3 == NO && ok == YES && ok1 == YES && ok2 == NO && ![[object description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed (1)"); 112 | 113 | 114 | BOOL ok4 = [self.class deswizzleInstanceMethod:@selector(a:)]; 115 | 116 | BOOL ok5 = [self.class deswizzleClassMethod:@selector(testRect)]; 117 | BOOL ok6 = [self.class deswizzleClassMethod:@selector(testRect2:)]; 118 | 119 | 120 | BOOL ok10 = deswizzleGlobal(); 121 | 122 | BOOL ok9 = [self.class deswizzleAllMethods]; 123 | 124 | BOOL ok8 = [self.class deswizzleAllInstanceMethods]; 125 | BOOL ok7 = [self.class deswizzleAllClassMethods]; 126 | 127 | 128 | XCTAssert(ok10 == NO && ok9 == NO && ok8 == NO && ok7 == NO && ok4 == YES && ok5 == YES && ok6 == YES && [self a:10] == 8, @"Deswizzling failed"); 129 | 130 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)), @"Deswizzling failed (1)"); 131 | 132 | 133 | XCTAssert(CGRectEqualToRect([self.class testRect2:CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), 10.0f, 10.0f)), @"Deswizzling failed (2)"); 134 | } 135 | 136 | - (void)removeSwizzles2:(NSObject *)object { 137 | BOOL ok2 = [object deswizzle]; 138 | BOOL ok3 = deswizzleInstances(); 139 | BOOL ok = [object deswizzleMethod:@selector(description)]; 140 | BOOL ok1 = [object deswizzleMethod:@selector(init)]; 141 | 142 | XCTAssert(ok3 == NO && ok == NO && ok1 == NO && ok2 == YES && ![[object description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed (1)"); 143 | 144 | 145 | BOOL ok6 = [self.class deswizzleInstanceMethod:@selector(a:)]; 146 | BOOL ok7 = [self.class deswizzleAllClassMethods]; 147 | 148 | BOOL ok10 = deswizzleGlobal(); 149 | 150 | BOOL ok9 = [self.class deswizzleAllMethods]; 151 | 152 | BOOL ok8 = [self.class deswizzleAllInstanceMethods]; 153 | 154 | 155 | BOOL ok4 = [self.class deswizzleClassMethod:@selector(testRect)]; 156 | BOOL ok5 = [self.class deswizzleClassMethod:@selector(testRect2:)]; 157 | 158 | 159 | XCTAssert(ok10 == NO && ok8 == NO && ok9 == NO && ok6 == YES && ok7 == YES && ok4 == NO && ok5 == NO && [self a:10] == 8, @"Deswizzling failed"); 160 | 161 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)), @"Deswizzling failed (1)"); 162 | 163 | 164 | XCTAssert(CGRectEqualToRect([self.class testRect2:CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), 10.0f, 10.0f)), @"Deswizzling failed (2)"); 165 | } 166 | 167 | 168 | 169 | - (void)removeSwizzles3:(NSObject *)object { 170 | BOOL ok3 = deswizzleInstances(); 171 | BOOL ok2 = [object deswizzle]; 172 | BOOL ok = [object deswizzleMethod:@selector(description)]; 173 | BOOL ok1 = [object deswizzleMethod:@selector(init)]; 174 | 175 | XCTAssert(ok3 == YES && ok == NO && ok1 == NO && ok2 == NO && ![[object description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed (1)"); 176 | 177 | 178 | BOOL ok6 = [self.class deswizzleAllInstanceMethods]; 179 | BOOL ok7 = [self.class deswizzleAllClassMethods]; 180 | 181 | BOOL ok10 = deswizzleGlobal(); 182 | 183 | BOOL ok9 = [self.class deswizzleAllMethods]; 184 | 185 | BOOL ok8 = [self.class deswizzleInstanceMethod:@selector(a:)]; 186 | 187 | BOOL ok4 = [self.class deswizzleClassMethod:@selector(testRect)]; 188 | BOOL ok5 = [self.class deswizzleClassMethod:@selector(testRect2:)]; 189 | 190 | 191 | XCTAssert(ok9 == NO && ok10 == NO && ok6 == YES && ok7 == YES && ok4 == NO && ok5 == NO && ok8 == NO && [self a:10] == 8, @"Deswizzling failed"); 192 | 193 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)), @"Deswizzling failed (1)"); 194 | 195 | 196 | XCTAssert(CGRectEqualToRect([self.class testRect2:CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), 10.0f, 10.0f)), @"Deswizzling failed (2)"); 197 | } 198 | 199 | 200 | 201 | - (void)removeSwizzles4:(NSObject *)object { 202 | BOOL ok3 = deswizzleInstances(); 203 | BOOL ok = [object deswizzleMethod:@selector(description)]; 204 | BOOL ok1 = [object deswizzleMethod:@selector(init)]; 205 | BOOL ok2 = [object deswizzle]; 206 | 207 | XCTAssert(ok3 == YES && ok == NO && ok1 == NO && ok2 == NO && ![[object description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed (1)"); 208 | 209 | 210 | BOOL ok9 = [self.class deswizzleAllMethods]; 211 | 212 | BOOL ok10 = deswizzleGlobal(); 213 | 214 | BOOL ok6 = [self.class deswizzleAllInstanceMethods]; 215 | BOOL ok7 = [self.class deswizzleAllClassMethods]; 216 | 217 | BOOL ok8 = [self.class deswizzleInstanceMethod:@selector(a:)]; 218 | 219 | BOOL ok4 = [self.class deswizzleClassMethod:@selector(testRect)]; 220 | BOOL ok5 = [self.class deswizzleClassMethod:@selector(testRect2:)]; 221 | 222 | 223 | XCTAssert(ok10 == NO && ok9 == YES && ok6 == NO && ok7 == NO && ok4 == NO && ok5 == NO && ok8 == NO && [self a:10] == 8, @"Deswizzling failed"); 224 | 225 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)), @"Deswizzling failed (1)"); 226 | 227 | 228 | XCTAssert(CGRectEqualToRect([self.class testRect2:CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), 10.0f, 10.0f)), @"Deswizzling failed (2)"); 229 | } 230 | 231 | 232 | 233 | - (void)removeSwizzles5:(NSObject *)object { 234 | BOOL ok3 = deswizzleInstances(); 235 | BOOL ok = [object deswizzleMethod:@selector(description)]; 236 | BOOL ok1 = [object deswizzleMethod:@selector(init)]; 237 | BOOL ok2 = [object deswizzle]; 238 | 239 | XCTAssert(ok3 == YES && ok == NO && ok1 == NO && ok2 == NO && ![[object description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed (1)"); 240 | 241 | 242 | BOOL ok10 = deswizzleGlobal(); 243 | 244 | BOOL ok9 = [self.class deswizzleAllMethods]; 245 | 246 | BOOL ok6 = [self.class deswizzleAllInstanceMethods]; 247 | BOOL ok7 = [self.class deswizzleAllClassMethods]; 248 | 249 | BOOL ok8 = [self.class deswizzleInstanceMethod:@selector(a:)]; 250 | 251 | BOOL ok4 = [self.class deswizzleClassMethod:@selector(testRect)]; 252 | BOOL ok5 = [self.class deswizzleClassMethod:@selector(testRect2:)]; 253 | 254 | 255 | XCTAssert(ok10 == YES && ok9 == NO && ok6 == NO && ok7 == NO && ok4 == NO && ok5 == NO && ok8 == NO && [self a:10] == 8, @"Deswizzling failed"); 256 | 257 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)), @"Deswizzling failed (1)"); 258 | 259 | 260 | XCTAssert(CGRectEqualToRect([self.class testRect2:CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), 10.0f, 10.0f)), @"Deswizzling failed (2)"); 261 | } 262 | 263 | 264 | 265 | - (void)removeSwizzles6:(NSObject *)object { 266 | BOOL ok11 = deswizzleAll(); 267 | 268 | 269 | BOOL ok3 = deswizzleInstances(); 270 | BOOL ok = [object deswizzleMethod:@selector(description)]; 271 | BOOL ok1 = [object deswizzleMethod:@selector(init)]; 272 | BOOL ok2 = [object deswizzle]; 273 | 274 | XCTAssert(ok11 == YES && ok3 == NO && ok == NO && ok1 == NO && ok2 == NO && ![[object description] hasSuffix:@"Only swizzled this instance"], @"Instance swizzling failed (1)"); 275 | 276 | 277 | BOOL ok10 = deswizzleGlobal(); 278 | 279 | BOOL ok9 = [self.class deswizzleAllMethods]; 280 | 281 | BOOL ok6 = [self.class deswizzleAllInstanceMethods]; 282 | BOOL ok7 = [self.class deswizzleAllClassMethods]; 283 | 284 | BOOL ok8 = [self.class deswizzleInstanceMethod:@selector(a:)]; 285 | 286 | BOOL ok4 = [self.class deswizzleClassMethod:@selector(testRect)]; 287 | BOOL ok5 = [self.class deswizzleClassMethod:@selector(testRect2:)]; 288 | 289 | 290 | XCTAssert(ok10 == NO && ok9 == NO && ok6 == NO && ok7 == NO && ok4 == NO && ok5 == NO && ok8 == NO && [self a:10] == 8, @"Deswizzling failed"); 291 | 292 | XCTAssert(CGRectEqualToRect([self.class testRect], CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)), @"Deswizzling failed (1)"); 293 | 294 | 295 | XCTAssert(CGRectEqualToRect([self.class testRect2:CGRectMake(0.0f, 1.0f, 2.0f, 3.0f)], CGRectInset(CGRectMake(0.0f, 1.0f, 2.0f, 3.0f), 10.0f, 10.0f)), @"Deswizzling failed (2)"); 296 | } 297 | 298 | //For debugging purposes: (function needs to be uncommented in JGMethodSwizzler.m in order to work) 299 | //FOUNDATION_EXTERN NSString *getStatus(); 300 | 301 | 302 | 303 | - (void)logStatusBefore { 304 | // NSLog(@"STATUS BEFORE %@", getStatus()); 305 | } 306 | 307 | - (void)logStatusAfter { 308 | // NSLog(@"STATUS AFTER %@", getStatus()); 309 | } 310 | 311 | - (void)logStatusFinal { 312 | // NSLog(@"STATUS FINAL %@", getStatus()); 313 | } 314 | 315 | 316 | - (void)testMain { 317 | [self logStatusBefore]; 318 | NSObject *object = [self applySwizzles]; 319 | [self logStatusAfter]; 320 | [self removeSwizzles1:object]; 321 | 322 | [self logStatusBefore]; 323 | object = [self applySwizzles]; 324 | [self logStatusAfter]; 325 | [self removeSwizzles2:object]; 326 | 327 | 328 | [self logStatusBefore]; 329 | object = [self applySwizzles]; 330 | [self logStatusAfter]; 331 | [self removeSwizzles3:object]; 332 | 333 | [self logStatusBefore]; 334 | object = [self applySwizzles]; 335 | [self logStatusAfter]; 336 | [self removeSwizzles4:object]; 337 | 338 | 339 | [self logStatusBefore]; 340 | object = [self applySwizzles]; 341 | [self logStatusAfter]; 342 | [self removeSwizzles5:object]; 343 | 344 | 345 | [self logStatusBefore]; 346 | object = [self applySwizzles]; 347 | [self logStatusAfter]; 348 | [self removeSwizzles6:object]; 349 | 350 | 351 | 352 | [self logStatusFinal]; 353 | } 354 | 355 | 356 | - (int)test:(int)a { 357 | NSLog(@"ORIGINAL"); 358 | return a+1; 359 | } 360 | 361 | - (void)testGlobalAndInstanceSwizzlingCombination1 { 362 | [self.class swizzleInstanceMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 363 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 364 | int orig = JGOriginalImplementation(int, a); 365 | NSLog(@"GLOBAL SWIZZLE"); 366 | return orig+1; 367 | }; 368 | }]; 369 | 370 | [self swizzleMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 371 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 372 | int orig = JGOriginalImplementation(int, a); 373 | NSLog(@"ISTANCE SWIZZLE"); 374 | return orig+1; 375 | }; 376 | }]; 377 | 378 | XCTAssert([self test:1] == 4, @"Integer mismatch"); 379 | 380 | BOOL ok = [self deswizzleMethod:@selector(test:)]; 381 | 382 | XCTAssert([self test:1] == 3, @"Integer mismatch"); 383 | 384 | BOOL ok1 = [self.class deswizzleInstanceMethod:@selector(test:)]; 385 | 386 | XCTAssert([self test:1] == 2, @"Integer mismatch"); 387 | 388 | XCTAssert(ok == YES && ok1 == YES, @"Deswizzling failed"); 389 | } 390 | 391 | 392 | - (void)testGlobalAndInstanceSwizzlingCombination2 { 393 | NSLog(@"Example for why global and instance swizzling is not a good combination"); 394 | [self swizzleMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 395 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 396 | int orig = JGOriginalImplementation(int, a); 397 | NSLog(@"ISTANCE SWIZZLE"); 398 | return orig+1; 399 | }; 400 | }]; 401 | 402 | //This swizzle would get put between the instance swizzle and the original method implementation. The instance specific swizzle however would not know that this happened and this swizzle would never be invoked on this specific instance. Therefore it throws an exception. 403 | XCTAssertThrows([self.class swizzleInstanceMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 404 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 405 | int orig = JGOriginalImplementation(int, a); 406 | NSLog(@"GLOBAL SWIZZLE"); 407 | return orig+1; 408 | }; 409 | }], @"Instance and global swizzle failure"); 410 | 411 | XCTAssert([self test:1] == 3, @"Integer mismatch"); 412 | 413 | BOOL ok = [self.class deswizzleInstanceMethod:@selector(test:)]; 414 | 415 | XCTAssert([self test:1] == 3, @"Integer mismatch"); 416 | 417 | BOOL ok1 = [self deswizzleMethod:@selector(test:)]; 418 | 419 | XCTAssert([self test:1] == 2, @"Integer mismatch"); 420 | 421 | XCTAssert(ok == NO && ok1 == YES, @"Deswizzling failed"); 422 | } 423 | 424 | 425 | - (void)testGlobalAndInstanceSwizzlingCombination3 { 426 | NSLog(@"Example for why global and instance swizzling is not a good combination"); 427 | [self swizzleMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 428 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 429 | int orig = JGOriginalImplementation(int, a); 430 | NSLog(@"ISTANCE SWIZZLE"); 431 | return orig+1; 432 | }; 433 | }]; 434 | 435 | //This swizzle would get put between the instance swizzle and the original method implementation. The instance specific swizzle however would not know that this happened and this swizzle would never be invoked on this specific instance. Therefore it throws an exception. 436 | XCTAssertThrows([self.class swizzleInstanceMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 437 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 438 | int orig = JGOriginalImplementation(int, a); 439 | NSLog(@"GLOBAL SWIZZLE"); 440 | return orig+1; 441 | }; 442 | }], @"Instance and global swizzle failure"); 443 | 444 | XCTAssert([self test:1] == 3, @"Integer mismatch"); 445 | 446 | BOOL ok = [self deswizzleMethod:@selector(test:)]; 447 | 448 | XCTAssert([self test:1] == 2, @"Integer mismatch"); 449 | 450 | BOOL ok1 = [self.class deswizzleInstanceMethod:@selector(test:)]; 451 | 452 | XCTAssert([self test:1] == 2, @"Integer mismatch"); 453 | 454 | 455 | XCTAssert(ok == YES && ok1 == NO, @"Deswizzling Failed"); 456 | } 457 | 458 | 459 | - (void)testGlobalAndInstanceSwizzlingCombination4 { 460 | [self.class swizzleInstanceMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 461 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 462 | int orig = JGOriginalImplementation(int, a); 463 | NSLog(@"GLOBAL SWIZZLE"); 464 | return orig+1; 465 | }; 466 | }]; 467 | 468 | [self swizzleMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 469 | return JGMethodReplacement(int, JGMethodSwizzlerTests *, int a) { 470 | int orig = JGOriginalImplementation(int, a); 471 | NSLog(@"ISTANCE SWIZZLE"); 472 | return orig+1; 473 | }; 474 | }]; 475 | 476 | XCTAssert([self test:1] == 4, @"Integer mismatch"); 477 | 478 | BOOL ok3 = [self deswizzle]; 479 | 480 | XCTAssert([self test:1] == 3, @"Integer mismatch"); 481 | 482 | BOOL ok4 = [self.class deswizzleAllMethods]; 483 | 484 | XCTAssert([self test:1] == 2, @"Integer mismatch"); 485 | 486 | BOOL ok = [self deswizzleMethod:@selector(test:)]; 487 | 488 | BOOL ok1 = [self.class deswizzleInstanceMethod:@selector(test:)]; 489 | 490 | XCTAssert(ok3 == YES && ok4 == YES && ok == NO && ok1 == NO, @"Deswizzling failed"); 491 | } 492 | 493 | 494 | @end 495 | 496 | -------------------------------------------------------------------------------- /JGMethodSwizzlerTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2014 Jonas Gessner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

JGMethodSwizzler

© 2013-2014 Jonas Gessner
2 | 3 | ---------------- 4 |
5 | 6 | An easy to use Objective-C API for swizzling class and instance methods, as well as swizzling instance methods on specific instances only. 7 | 8 | Setup 9 | ===== 10 | CocoaPods:
11 | Add this to your `Podfile`: 12 | ``` 13 | pod 'JGMethodSwizzler', '2.0.1' 14 | ``` 15 |

16 | OR: 17 |

18 | Add source Files:
19 | 1. Add the `JGMethodSwizzler` folder to your Xcode Project.
20 | 2. `#import "JGMethodSwizzler.h"`. 21 | 22 | Documentation 23 | ============= 24 | #####For further examples see the `JGMethodSwizzlerTests` Xcode project. 25 | 26 | 27 | JGMethodSwizzler can be used for three basic swizzling types: Swizzling a specific method for all instances of a class, swizzling class methods and swizzling instance methods of specific instances only. 28 | 29 | JGMethodSwizzler is completely thread safe and can handle multiple swizzles. Instance-specific swizzling should however not be combined with global swizzling in the same method. 30 | 31 | 32 | 33 | ###Swizzling a class method: 34 | Swizzling the method `+(int)[TestClass test:(int)]` 35 | ```objc 36 | [TestClass swizzleClassMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 37 | //return a replacement block 38 | return JGMethodReplacement(int, const Class *, int arg) { 39 | //get the original value 40 | int orig = JGOriginalImplementation(int, arg); 41 | //return the modified value 42 | return orig+2; 43 | }; 44 | }]; 45 | ``` 46 | 47 | After this code is run, calling the method will return the modified value until the method is deswizzled. 48 | 49 | 50 | ###Swizzling an instance method across all instances of a class: 51 | Swizzling the method `-(int)[TestClass test:(int)]` 52 | ```objc 53 | [TestClass swizzleInstanceMethod:@selector(test:) withReplacement:JGMethodReplacementProviderBlock { 54 | //return a replacement block 55 | return JGMethodReplacement(int, TestClass *, int arg) { 56 | //get the original value 57 | int orig = JGOriginalImplementation(int, arg); 58 | //return the modified value 59 | return orig+2; 60 | }; 61 | }]; 62 | ``` 63 | 64 | After this code is run, calling the method will return the modified value until the method is deswizzled. 65 | 66 | 67 | 68 | ###Swizzling an instance method for a specific instance: 69 | Swizzling the `description` method on a specific `NSObject`instance: 70 | ```objc 71 | NSObject *object = [NSObject new]; 72 | 73 | [object swizzleMethod:@selector(description) withReplacement:JGMethodReplacementProviderBlock { 74 | return JGMethodReplacement(NSString *, NSObject *) { 75 | NSString *orig = JGOriginalImplementation(NSString *); 76 | 77 | return [orig stringByAppendingString:@" Swizzled!!"]; 78 | }; 79 | }]; 80 | ``` 81 | 82 | After this code is run, calling the method will return the modified value until the method is deswizzled. 83 | 84 | 85 | ###Deswizzling 86 | 87 | All swizzles can be removed once they've been applied. 88 | 89 | 90 | `deswizzleAll()` removes all swizzles. 91 | 92 | 93 | ####Deswizzling global class and instance swizzles 94 | 95 | `deswizzleGlobal()` removes all swizzles that have been applied as global swizzles (not instance specific). 96 | 97 | `+deswizzleClassMethod:(SEL)` deswizzles a specific class method. 98 | 99 | `+deswizzleInstanceMethod:(SEL)` deswizzles a specific instance method. 100 | 101 | `+deswizzleAllClassMethods` deswizzles all swizzled class methods of this class. 102 | 103 | `+deswizzleAllInstanceMethods` deswizzles all swizzled instance methods of this class. 104 | 105 | `+deswizzleAllMethods` deswizzles all swizzled methods of this class. 106 | 107 | 108 | ####Deswizzling Instance specific swizzles 109 | 110 | `deswizzleInstances()` removes all swizzles that have been applied as instance specific swizzles. 111 | 112 | `-deswizzleMethod:(SEL)` deswizzles a specific instance method of this instance. 113 | 114 | `-deswizzle` deswizzles all swizzled instance methods of this instance. 115 | 116 | 117 | Notes 118 | ======= 119 | `JGMethodSwizzler` works with both ARC and MRC/MRR. 120 | 121 | Credits 122 | ========= 123 | Created by Jonas Gessner. ©2013-2014 124 | 125 | Thanks to Andrew Richardson for his inspiration and contribution with `InstanceHook`. 126 | 127 | License 128 | ========== 129 | Licensed under the MIT license. 130 | --------------------------------------------------------------------------------