Skip to content

Instantly share code, notes, and snippets.

@mikeash
Created February 4, 2018 03:26
Show Gist options
  • Save mikeash/c51b5b31f672a8ef967f203c4dee4e98 to your computer and use it in GitHub Desktop.
Save mikeash/c51b5b31f672a8ef967f203c4dee4e98 to your computer and use it in GitHub Desktop.
@import Foundation;
@import ObjectiveC;
static NSMutableSet *swizzledClasses;
static NSMutableDictionary *swizzledBlocks; // Class -> SEL (as string) -> block
static IMP forwardingIMP;
static dispatch_once_t once;
void Swizzle(Class c, SEL sel, void (^block)(NSInvocation *)) {
dispatch_once(&once, ^{
swizzledClasses = [NSMutableSet set];
swizzledBlocks = [NSMutableDictionary dictionary];
forwardingIMP = class_getMethodImplementation([NSObject class], @selector(thisReallyShouldNotExistItWouldBeExtremelyWeirdIfItDid));
});
if(![swizzledClasses containsObject: c]) {
SEL fwdSel = @selector(forwardInvocation:);
Method m = class_getInstanceMethod(c, fwdSel);
__block IMP orig;
IMP imp = imp_implementationWithBlock(^(id self, NSInvocation *invocation) {
NSString *selStr = NSStringFromSelector([invocation selector]);
void (^block)(NSInvocation *) = swizzledBlocks[c][selStr];
if(block != nil) {
NSString *originalStr = [@"omniswizzle_" stringByAppendingString: selStr];
[invocation setSelector: NSSelectorFromString(originalStr)];
block(invocation);
} else {
((void (*)(id, SEL, NSInvocation *))orig)(self, fwdSel, invocation);
}
});
orig = method_setImplementation(m, imp);
[swizzledClasses addObject: c];
}
NSMutableDictionary *classDict = swizzledBlocks[c];
if(classDict == nil) {
classDict = [NSMutableDictionary dictionary];
swizzledBlocks[(id)c] = classDict;
}
classDict[NSStringFromSelector(sel)] = block;
Method m = class_getInstanceMethod(c, sel);
NSString *newSelStr = [@"omniswizzle_" stringByAppendingString: NSStringFromSelector(sel)];
SEL newSel = NSSelectorFromString(newSelStr);
class_addMethod(c, newSel, method_getImplementation(m), method_getTypeEncoding(m));
method_setImplementation(m, forwardingIMP);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Swizzle([NSBundle class], @selector(objectForInfoDictionaryKey:), ^(NSInvocation *inv) {
NSLog(@"invocation is %@ - calling now", inv);
[inv invoke];
NSLog(@"after");
});
NSLog(@"%@", [[NSBundle bundleForClass: [NSString class]] objectForInfoDictionaryKey: (__bridge NSString *)kCFBundleVersionKey]);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment