Просмотр исходного кода

Move swizzling helpers into FLEXUtility

Ryan Olson лет назад: 10
Родитель
Сommit
3dd27557ea

+ 39 - 103
Classes/Network/PonyDebugger/FLEXNetworkObserver.m

@@ -14,6 +14,7 @@
 
 #import "FLEXNetworkObserver.h"
 #import "FLEXNetworkRecorder.h"
+#import "FLEXUtility.h"
 
 #import <objc/runtime.h>
 #import <objc/message.h>
@@ -127,11 +128,6 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
 
 #pragma mark Delegate Injection Convenience Methods
 
-+ (SEL)swizzledSelectorForSelector:(SEL)selector
-{
-    return NSSelectorFromString([NSString stringWithFormat:@"_flex_swizzle_%x_%@", arc4random(), NSStringFromSelector(selector)]);
-}
-
 /// All swizzled delegate methods should make use of this guard.
 /// This will prevent duplicated sniffing when the original implementation calls up to a superclass implementation which we've also swizzled.
 /// The superclass implementation (and implementations in classes above that) will be executed without inteference if called from the original implementation.
@@ -158,66 +154,6 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
     objc_setAssociatedObject(object, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
 }
 
-+ (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector class:(Class)cls
-{
-    if ([cls instancesRespondToSelector:selector]) {
-        unsigned int numMethods = 0;
-        Method *methods = class_copyMethodList(cls, &numMethods);
-        
-        BOOL implementsSelector = NO;
-        for (int index = 0; index < numMethods; index++) {
-            SEL methodSelector = method_getName(methods[index]);
-            if (selector == methodSelector) {
-                implementsSelector = YES;
-                break;
-            }
-        }
-        
-        free(methods);
-        
-        if (!implementsSelector) {
-            return YES;
-        }
-    }
-    
-    return NO;
-}
-
-+ (void)replaceImplementationOfKnownSelector:(SEL)originalSelector onClass:(Class)class withBlock:(id)block swizzledSelector:(SEL)swizzledSelector
-{
-    // This method is only intended for swizzling methods that are know to exist on the class.
-    // Bail if that isn't the case.
-    Method originalMethod = class_getInstanceMethod(class, originalSelector);
-    if (!originalMethod) {
-        return;
-    }
-
-    IMP implementation = imp_implementationWithBlock(block);
-    class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalMethod));
-    Method newMethod = class_getInstanceMethod(class, swizzledSelector);
-    method_exchangeImplementations(originalMethod, newMethod);
-}
-
-+ (void)replaceImplementationOfSelector:(SEL)selector withSelector:(SEL)swizzledSelector forClass:(Class)cls withMethodDescription:(struct objc_method_description)methodDescription implementationBlock:(id)implementationBlock undefinedBlock:(id)undefinedBlock
-{
-    if ([self instanceRespondsButDoesNotImplementSelector:selector class:cls]) {
-        return;
-    }
-
-    IMP implementation = imp_implementationWithBlock((id)([cls instancesRespondToSelector:selector] ? implementationBlock : undefinedBlock));
-    
-    Method oldMethod = class_getInstanceMethod(cls, selector);
-    if (oldMethod) {
-        class_addMethod(cls, swizzledSelector, implementation, methodDescription.types);
-         
-        Method newMethod = class_getInstanceMethod(cls, swizzledSelector);
-        
-        method_exchangeImplementations(oldMethod, newMethod);
-    } else {
-        class_addMethod(cls, selector, implementation, methodDescription.types);
-    }
-}
-
 #pragma mark - Delegate Injection
 
 + (void)injectIntoAllNSURLConnectionDelegateClasses
@@ -321,7 +257,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
     dispatch_once(&onceToken, ^{
         Class class = [NSURLConnection class];
         SEL selector = @selector(cancel);
-        SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+        SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
         Method originalCancel = class_getInstanceMethod(class, selector);
 
         void (^swizzleBlock)(NSURLConnection *) = ^(NSURLConnection *slf) {
@@ -352,7 +288,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
             class = NSClassFromString([@[@"__", @"NSC", @"FURLS", @"ession", @"Task"] componentsJoinedByString:@""]);
         }
         SEL selector = @selector(resume);
-        SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+        SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
         Method originalResume = class_getInstanceMethod(class, selector);
 
@@ -374,7 +310,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
     dispatch_once(&onceToken, ^{
         Class class = objc_getMetaClass(class_getName([NSURLConnection class]));
         SEL selector = @selector(sendAsynchronousRequest:queue:completionHandler:);
-        SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+        SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
         typedef void (^NSURLConnectionAsyncCompletion)(NSURLResponse* response, NSData* data, NSError* connectionError);
 
@@ -404,7 +340,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
             }
         };
         
-        [self replaceImplementationOfKnownSelector:selector onClass:class withBlock:asyncSwizzleBlock swizzledSelector:swizzledSelector];
+        [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:asyncSwizzleBlock swizzledSelector:swizzledSelector];
     });
 }
 
@@ -414,7 +350,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
     dispatch_once(&onceToken, ^{
         Class class = objc_getMetaClass(class_getName([NSURLConnection class]));
         SEL selector = @selector(sendSynchronousRequest:returningResponse:error:);
-        SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+        SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
         NSData *(^syncSwizzleBlock)(Class, NSURLRequest *, NSURLResponse **, NSError **) = ^NSData *(Class slf, NSURLRequest *request, NSURLResponse **response, NSError **error) {
             NSData *data = nil;
@@ -446,7 +382,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
             return data;
         };
         
-        [self replaceImplementationOfKnownSelector:selector onClass:class withBlock:syncSwizzleBlock swizzledSelector:swizzledSelector];
+        [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:syncSwizzleBlock swizzledSelector:swizzledSelector];
     });
 }
 
@@ -469,9 +405,9 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
 
         for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) {
             SEL selector = selectors[selectorIndex];
-            SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+            SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
-            if ([self instanceRespondsButDoesNotImplementSelector:selector class:class]) {
+            if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) {
                 // iOS 7 does not implement these methods on NSURLSession. We actually want to
                 // swizzle __NSCFURLSession, which we can get from the class of the shared session
                 class = [[NSURLSession sharedSession] class];
@@ -494,7 +430,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
                 return task;
             };
             
-            [self replaceImplementationOfKnownSelector:selector onClass:class withBlock:asyncDataOrDownloadSwizzleBlock swizzledSelector:swizzledSelector];
+            [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:asyncDataOrDownloadSwizzleBlock swizzledSelector:swizzledSelector];
         }
     });
 }
@@ -516,9 +452,9 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
 
         for (int selectorIndex = 0; selectorIndex < numSelectors; selectorIndex++) {
             SEL selector = selectors[selectorIndex];
-            SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+            SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
-            if ([self instanceRespondsButDoesNotImplementSelector:selector class:class]) {
+            if ([FLEXUtility instanceRespondsButDoesNotImplementSelector:selector class:class]) {
                 // iOS 7 does not implement these methods on NSURLSession. We actually want to
                 // swizzle __NSCFURLSession, which we can get from the class of the shared session
                 class = [[NSURLSession sharedSession] class];
@@ -538,7 +474,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
                 return task;
             };
             
-            [self replaceImplementationOfKnownSelector:selector onClass:class withBlock:asyncUploadTaskSwizzleBlock swizzledSelector:swizzledSelector];
+            [FLEXUtility replaceImplementationOfKnownSelector:selector onClass:class withBlock:asyncUploadTaskSwizzleBlock swizzledSelector:swizzledSelector];
         }
     });
 }
@@ -577,7 +513,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
 + (void)injectWillSendRequestIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(connection:willSendRequest:redirectResponse:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
     if (!protocol) {
@@ -603,13 +539,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         return returnValue;
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectDidReceiveResponseIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(connection:didReceiveResponse:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
     if (!protocol) {
@@ -632,13 +568,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectDidReceiveDataIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(connection:didReceiveData:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
     if (!protocol) {
@@ -661,13 +597,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectDidFinishLoadingIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(connectionDidFinishLoading:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLConnectionDataDelegate);
     if (!protocol) {
@@ -690,13 +626,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectDidFailWithErrorIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(connection:didFailWithError:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLConnectionDelegate);
     struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
@@ -715,13 +651,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectTaskWillPerformHTTPRedirectionIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
     Protocol *protocol = @protocol(NSURLSessionTaskDelegate);
 
@@ -741,14 +677,14 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
 
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 
 }
 
 + (void)injectTaskDidReceiveDataIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:dataTask:didReceiveData:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLSessionDataDelegate);
     
@@ -768,14 +704,14 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 
 }
 
 + (void)injectDataTaskDidBecomeDownloadTaskIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:dataTask:didBecomeDownloadTask:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
     Protocol *protocol = @protocol(NSURLSessionDataDelegate);
 
@@ -795,13 +731,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
 
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectTaskDidReceiveResponseIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:dataTask:didReceiveResponse:completionHandler:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLSessionDataDelegate);
     
@@ -821,14 +757,14 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
     
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 
 }
 
 + (void)injectTaskDidCompleteWithErrorIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:task:didCompleteWithError:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
     
     Protocol *protocol = @protocol(NSURLSessionTaskDelegate);
     struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
@@ -847,14 +783,14 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
 
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 // Used for overriding AFNetworking behavior
 + (void)injectRespondsToSelectorIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(respondsToSelector:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
     //Protocol *protocol = @protocol(NSURLSessionTaskDelegate);
     Method method = class_getInstanceMethod(cls, selector);
@@ -871,14 +807,14 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         return ((BOOL(*)(id, SEL, SEL))objc_msgSend)(slf, swizzledSelector, sel);
     };
 
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 
 + (void)injectDownloadTaskDidFinishDownloadingIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:downloadTask:didFinishDownloadingToURL:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
     Protocol *protocol = @protocol(NSURLSessionDownloadDelegate);
     struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
@@ -898,13 +834,13 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
 
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 }
 
 + (void)injectDownloadTaskDidWriteDataIntoDelegateClass:(Class)cls
 {
     SEL selector = @selector(URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:);
-    SEL swizzledSelector = [self swizzledSelectorForSelector:selector];
+    SEL swizzledSelector = [FLEXUtility swizzledSelectorForSelector:selector];
 
     Protocol *protocol = @protocol(NSURLSessionDownloadDelegate);
     struct objc_method_description methodDescription = protocol_getMethodDescription(protocol, selector, NO, YES);
@@ -923,7 +859,7 @@ didBecomeDownloadTask:(NSURLSessionDownloadTask *)downloadTask delegate:(id <NSU
         }];
     };
 
-    [self replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
+    [FLEXUtility replaceImplementationOfSelector:selector withSelector:swizzledSelector forClass:cls withMethodDescription:methodDescription implementationBlock:implementationBlock undefinedBlock:undefinedBlock];
 
 }
 

+ 7 - 0
Classes/Utility/FLEXUtility.h

@@ -38,4 +38,11 @@
 + (BOOL)isValidJSONData:(NSData *)data;
 + (NSData *)inflatedDataFromCompressedData:(NSData *)compressedData;
 
+// Swizzling utilities
+
++ (SEL)swizzledSelectorForSelector:(SEL)selector;
++ (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector class:(Class)cls;
++ (void)replaceImplementationOfKnownSelector:(SEL)originalSelector onClass:(Class)class withBlock:(id)block swizzledSelector:(SEL)swizzledSelector;
++ (void)replaceImplementationOfSelector:(SEL)selector withSelector:(SEL)swizzledSelector forClass:(Class)cls withMethodDescription:(struct objc_method_description)methodDescription implementationBlock:(id)implementationBlock undefinedBlock:(id)undefinedBlock;
+
 @end

+ 66 - 0
Classes/Utility/FLEXUtility.m

@@ -10,6 +10,7 @@
 #import "FLEXResources.h"
 #import <ImageIO/ImageIO.h>
 #import <zlib.h>
+#import <objc/runtime.h>
 
 @implementation FLEXUtility
 
@@ -325,4 +326,69 @@
     return inflatedData;
 }
 
++ (SEL)swizzledSelectorForSelector:(SEL)selector
+{
+    return NSSelectorFromString([NSString stringWithFormat:@"_flex_swizzle_%x_%@", arc4random(), NSStringFromSelector(selector)]);
+}
+
++ (BOOL)instanceRespondsButDoesNotImplementSelector:(SEL)selector class:(Class)cls
+{
+    if ([cls instancesRespondToSelector:selector]) {
+        unsigned int numMethods = 0;
+        Method *methods = class_copyMethodList(cls, &numMethods);
+        
+        BOOL implementsSelector = NO;
+        for (int index = 0; index < numMethods; index++) {
+            SEL methodSelector = method_getName(methods[index]);
+            if (selector == methodSelector) {
+                implementsSelector = YES;
+                break;
+            }
+        }
+        
+        free(methods);
+        
+        if (!implementsSelector) {
+            return YES;
+        }
+    }
+    
+    return NO;
+}
+
++ (void)replaceImplementationOfKnownSelector:(SEL)originalSelector onClass:(Class)class withBlock:(id)block swizzledSelector:(SEL)swizzledSelector
+{
+    // This method is only intended for swizzling methods that are know to exist on the class.
+    // Bail if that isn't the case.
+    Method originalMethod = class_getInstanceMethod(class, originalSelector);
+    if (!originalMethod) {
+        return;
+    }
+    
+    IMP implementation = imp_implementationWithBlock(block);
+    class_addMethod(class, swizzledSelector, implementation, method_getTypeEncoding(originalMethod));
+    Method newMethod = class_getInstanceMethod(class, swizzledSelector);
+    method_exchangeImplementations(originalMethod, newMethod);
+}
+
++ (void)replaceImplementationOfSelector:(SEL)selector withSelector:(SEL)swizzledSelector forClass:(Class)cls withMethodDescription:(struct objc_method_description)methodDescription implementationBlock:(id)implementationBlock undefinedBlock:(id)undefinedBlock
+{
+    if ([self instanceRespondsButDoesNotImplementSelector:selector class:cls]) {
+        return;
+    }
+    
+    IMP implementation = imp_implementationWithBlock((id)([cls instancesRespondToSelector:selector] ? implementationBlock : undefinedBlock));
+    
+    Method oldMethod = class_getInstanceMethod(cls, selector);
+    if (oldMethod) {
+        class_addMethod(cls, swizzledSelector, implementation, methodDescription.types);
+        
+        Method newMethod = class_getInstanceMethod(cls, swizzledSelector);
+        
+        method_exchangeImplementations(oldMethod, newMethod);
+    } else {
+        class_addMethod(cls, selector, implementation, methodDescription.types);
+    }
+}
+
 @end