Skip to content

Commit

Permalink
Implemented actual hooks and included new examples
Browse files Browse the repository at this point in the history
  • Loading branch information
adamszedelyi committed Feb 26, 2017
1 parent 2895c7c commit 91fb618
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 9 deletions.
10 changes: 9 additions & 1 deletion ASHookIt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
6DCD89831E63676500F620D2 /* ASHook.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DCD89811E63676500F620D2 /* ASHook.m */; };
6DCD89861E63678B00F620D2 /* ASHook+MethodSwizzler.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DCD89841E63678B00F620D2 /* ASHook+MethodSwizzler.h */; settings = {ATTRIBUTES = (Public, ); }; };
6DCD89871E63678B00F620D2 /* ASHook+MethodSwizzler.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DCD89851E63678B00F620D2 /* ASHook+MethodSwizzler.m */; };
6DCD898A1E636A2000F620D2 /* ASHook+BlockInsert.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DCD89881E636A2000F620D2 /* ASHook+BlockInsert.h */; settings = {ATTRIBUTES = (Public, ); }; };
6DCD898B1E636A2000F620D2 /* ASHook+BlockInsert.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DCD89891E636A2000F620D2 /* ASHook+BlockInsert.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -37,6 +39,8 @@
6DCD89811E63676500F620D2 /* ASHook.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASHook.m; sourceTree = "<group>"; };
6DCD89841E63678B00F620D2 /* ASHook+MethodSwizzler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASHook+MethodSwizzler.h"; sourceTree = "<group>"; };
6DCD89851E63678B00F620D2 /* ASHook+MethodSwizzler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASHook+MethodSwizzler.m"; sourceTree = "<group>"; };
6DCD89881E636A2000F620D2 /* ASHook+BlockInsert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASHook+BlockInsert.h"; sourceTree = "<group>"; };
6DCD89891E636A2000F620D2 /* ASHook+BlockInsert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "ASHook+BlockInsert.m"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -84,6 +88,8 @@
6DCD89811E63676500F620D2 /* ASHook.m */,
6DCD89841E63678B00F620D2 /* ASHook+MethodSwizzler.h */,
6DCD89851E63678B00F620D2 /* ASHook+MethodSwizzler.m */,
6DCD89881E636A2000F620D2 /* ASHook+BlockInsert.h */,
6DCD89891E636A2000F620D2 /* ASHook+BlockInsert.m */,
6DCD892F1E63091F00F620D2 /* Info.plist */,
);
path = ASHookIt;
Expand All @@ -105,9 +111,10 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6DCD893C1E63091F00F620D2 /* ASHookIt.h in Headers */,
6DCD89821E63676500F620D2 /* ASHook.h in Headers */,
6DCD89861E63678B00F620D2 /* ASHook+MethodSwizzler.h in Headers */,
6DCD893C1E63091F00F620D2 /* ASHookIt.h in Headers */,
6DCD898A1E636A2000F620D2 /* ASHook+BlockInsert.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -210,6 +217,7 @@
buildActionMask = 2147483647;
files = (
6DCD89871E63678B00F620D2 /* ASHook+MethodSwizzler.m in Sources */,
6DCD898B1E636A2000F620D2 /* ASHook+BlockInsert.m in Sources */,
6DCD89831E63676500F620D2 /* ASHook.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
33 changes: 33 additions & 0 deletions ASHookIt/ASHook+BlockInsert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// ASHook+BlockInsert.h
// ASHookIt
//
// Created by Adam Szedelyi on 2017. 02. 26..
// Copyright © 2017. Adam Szedelyi. All rights reserved.
//

#import <ASHookIt/ASHookIt.h>

@interface ASHook (BlockInsert)

/**
* Runs the given block on the target just before the given selector gets called.
*
* @param block The block that will run.
* @param targetInstance The instance where you want to run the block.
* @param originalSelector The original selector that will trigger the block and run after the block.
*
*/
+ (void)runBlock:(void (^)(__unsafe_unretained id _self))block onTarget:(id)targetInstance beforeInstanceSelector:(SEL)originalSelector;

/**
* Runs the given block on the target just before the given selector gets called.
*
* @param block The block that will run.
* @param targetClass The class where you want to run the block.
* @param originalSelector The original selector that will trigger the block and run after the block.
*
*/
+ (void)runBlock:(void (^)(__unsafe_unretained id _self))block onTarget:(id)targetClass beforeClassSelector:(SEL)originalSelector;

@end
73 changes: 73 additions & 0 deletions ASHookIt/ASHook+BlockInsert.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//
// ASHook+BlockInsert.m
// ASHookIt
//
// Created by Adam Szedelyi on 2017. 02. 26..
// Copyright © 2017. Adam Szedelyi. All rights reserved.
//

#import "ASHook+BlockInsert.h"
#import "ASHook+MethodSwizzler.h"
#import <objc/runtime.h>
#import <objc/message.h>

@interface ASHook ()

+ (void)swizzle:(id)target
selector:(SEL)originalSelector
newSelector:(SEL)newSelector
originalMethod:(Method)originalMethod
newMethod:(Method)newMethod;

@end

@implementation ASHook (BlockInsert)

#pragma mark - Public methods

+ (void)runBlock:(void (^)(__unsafe_unretained id _self))block onTarget:(id)targetInstance beforeInstanceSelector:(SEL)originalSelector {
id target = [targetInstance class];
Method originalMethod = class_getInstanceMethod(target, originalSelector);
SEL newSelector = [self insertBlock:block onTarget:target originalSelector:originalSelector originalMethod:originalMethod];
if (newSelector) {
Method newMethod = class_getInstanceMethod(target, newSelector);
[ASHook swizzle:target selector:originalSelector newSelector:newSelector originalMethod:originalMethod newMethod:newMethod];
}
}

+ (void)runBlock:(void (^)(__unsafe_unretained id _self))block onTarget:(id)targetClass beforeClassSelector:(SEL)originalSelector {
id target = object_getClass(targetClass);
Method originalMethod = class_getClassMethod(target, originalSelector);
SEL newSelector = [self insertBlock:block onTarget:target originalSelector:originalSelector originalMethod:originalMethod];
if (newSelector) {
Method newMethod = class_getClassMethod(target, newSelector);
[ASHook swizzle:target selector:originalSelector newSelector:newSelector originalMethod:originalMethod newMethod:newMethod];
}
}

#pragma mark - Private methods

/*! @brief Creating and adding a new implementation for the original method with the block included. */
+ (SEL)insertBlock:(void (^)(__unsafe_unretained id _self))block
onTarget:(id)target
originalSelector:(SEL)originalSelector
originalMethod:(Method)originalMethod {
NSString *swizzledMethodSuffix = @"_ASMethodSwizzled";
NSString *swizzledMethodName = [NSString stringWithFormat:@"%@%@", NSStringFromSelector(originalSelector), swizzledMethodSuffix];
SEL newSelector = NSSelectorFromString(swizzledMethodName);
id actionBlock = ^(__unsafe_unretained id _self) {
if (block != nil) {
block(_self);
}
((void ( *)(id, SEL))objc_msgSend)(_self, newSelector); // Using ObjC messaging directly to avoid ARC messing with the lifecycle of the original object. (ie. had issues with some classes' dealloc otherwise)
};
IMP impl = imp_implementationWithBlock(actionBlock);
const char *encoding = method_getTypeEncoding(originalMethod);
if (!class_addMethod(target, newSelector, impl, encoding)) {
NSLog(@"Failed to add method: %@ on %@", NSStringFromSelector(newSelector), target);
return nil;
}
return newSelector;
}

@end
2 changes: 1 addition & 1 deletion ASHookIt/ASHook+MethodSwizzler.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ @implementation ASHook (MethodSwizzler)
#pragma mark - Public methods

+ (void)swizzle:(id)targetInstance instanceSelector:(SEL)originalSelector withInstanceSelector:(SEL)newSelector {
id target = object_getClass(targetInstance);
id target = [targetInstance class];
Method originalMethod = class_getInstanceMethod(target, originalSelector);
Method newMethod = class_getInstanceMethod(target, newSelector);
[self swizzle:target selector:originalSelector newSelector:newSelector originalMethod:originalMethod newMethod:newMethod];
Expand Down
1 change: 1 addition & 0 deletions ASHookIt/ASHookIt.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import <UIKit/UIKit.h>
#import <ASHookIt/ASHook.h>
#import <ASHookIt/ASHook+MethodSwizzler.h>
#import <ASHookIt/ASHook+BlockInsert.h>

//! Project version number for ASHookIt.
FOUNDATION_EXPORT double ASHookItVersionNumber;
Expand Down
21 changes: 14 additions & 7 deletions ASHookItDemo/ASHookItDemo/FirstViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,30 @@ @implementation FirstViewController
#pragma mark - Swizzling example 1

+ (void)initialize {
// A simple class method swizzling example.
// Example 1: simple instance method swizzling
[ASHook swizzle:self instanceSelector:@selector(logHelloWorld) withInstanceSelector:@selector(logGoodbyeWorld)];

// Example 2: class method swizzling
[ASHook swizzle:self classSelector:@selector(alloc) withClassSelector:@selector(swizzledAlloc)];

// Example 3: an instance method hook (before it runs) - the scope can be even all UIViewController objects
[ASHook runBlock:^(__unsafe_unretained id _self) {
NSLog(@"viewDidLoad will be called on %@!", _self);
} onTarget:[UIViewController class] beforeInstanceSelector:@selector(viewDidLoad)];

// Example 4: a class method hook (before it runs)
[ASHook runBlock:^(__unsafe_unretained id _self) {
NSLog(@"alloc will run - will there be any funny messages in it?");
} onTarget:self beforeClassSelector:@selector(alloc)];
}

+ (instancetype)swizzledAlloc {
NSLog(@"Whoah! Swizzled allocation of this class is cool!");
return [super alloc];
}

#pragma mark - Swizzling example 2

- (void)viewDidLoad {
[super viewDidLoad];

// A simple instance method swizzling example.
[ASHook swizzle:self instanceSelector:@selector(logHelloWorld) withInstanceSelector:@selector(logGoodbyeWorld)];

[self logHelloWorld];
}

Expand Down

0 comments on commit 91fb618

Please sign in to comment.