Skip to content

Commit

Permalink
Implemented method swizzling and included examples
Browse files Browse the repository at this point in the history
  • Loading branch information
adamszedelyi committed Feb 26, 2017
1 parent 803bdf7 commit 2895c7c
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 40 deletions.
18 changes: 18 additions & 0 deletions ASHookIt.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
6DCD89351E63091F00F620D2 /* ASHookIt.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6DCD892B1E63091F00F620D2 /* ASHookIt.framework */; };
6DCD893A1E63091F00F620D2 /* ASHookItTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6DCD89391E63091F00F620D2 /* ASHookItTests.m */; };
6DCD893C1E63091F00F620D2 /* ASHookIt.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DCD892E1E63091F00F620D2 /* ASHookIt.h */; settings = {ATTRIBUTES = (Public, ); }; };
6DCD89821E63676500F620D2 /* ASHook.h in Headers */ = {isa = PBXBuildFile; fileRef = 6DCD89801E63676500F620D2 /* ASHook.h */; settings = {ATTRIBUTES = (Public, ); }; };
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 */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand All @@ -29,6 +33,10 @@
6DCD89341E63091F00F620D2 /* ASHookItTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ASHookItTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
6DCD89391E63091F00F620D2 /* ASHookItTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ASHookItTests.m; sourceTree = "<group>"; };
6DCD893B1E63091F00F620D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6DCD89801E63676500F620D2 /* ASHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASHook.h; sourceTree = "<group>"; };
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>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -72,6 +80,10 @@
isa = PBXGroup;
children = (
6DCD892E1E63091F00F620D2 /* ASHookIt.h */,
6DCD89801E63676500F620D2 /* ASHook.h */,
6DCD89811E63676500F620D2 /* ASHook.m */,
6DCD89841E63678B00F620D2 /* ASHook+MethodSwizzler.h */,
6DCD89851E63678B00F620D2 /* ASHook+MethodSwizzler.m */,
6DCD892F1E63091F00F620D2 /* Info.plist */,
);
path = ASHookIt;
Expand All @@ -93,6 +105,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6DCD89821E63676500F620D2 /* ASHook.h in Headers */,
6DCD89861E63678B00F620D2 /* ASHook+MethodSwizzler.h in Headers */,
6DCD893C1E63091F00F620D2 /* ASHookIt.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -195,6 +209,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6DCD89871E63678B00F620D2 /* ASHook+MethodSwizzler.m in Sources */,
6DCD89831E63676500F620D2 /* ASHook.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -386,6 +402,7 @@
6DCD89411E63091F00F620D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6DCD89421E63091F00F620D2 /* Build configuration list for PBXNativeTarget "ASHookItTests" */ = {
isa = XCConfigurationList;
Expand All @@ -394,6 +411,7 @@
6DCD89441E63091F00F620D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
33 changes: 33 additions & 0 deletions ASHookIt/ASHook+MethodSwizzler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// ASHook+MethodSwizzler.h
// ASHookIt
//
// Created by Adam Szedelyi on 2017. 02. 26..
// Copyright © 2017. Adam Szedelyi. All rights reserved.
//

#import "ASHook.h"

@interface ASHook (MethodSwizzler)

/**
* Replaces the implementations of the two given selectors of an instance.
*
* @param targetInstance The instance where you want to replace selectors.
* @param originalSelector The original selector that is to be replaced.
* @param newSelector The new selector will replace the original selector's implementation.
*
*/
+ (void)swizzle:(id)targetInstance instanceSelector:(SEL)originalSelector withInstanceSelector:(SEL)newSelector;

/**
* Replaces the implementations of the two given selectors of a class.
*
* @param targetClass The class where you want to replace selectors.
* @param originalSelector The original selector that is to be replaced.
* @param newSelector The new selector will replace the original selector's implementation.
*
*/
+ (void)swizzle:(id)targetClass classSelector:(SEL)originalSelector withClassSelector:(SEL)newSelector;

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

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

@implementation ASHook (MethodSwizzler)

#pragma mark - Public methods

+ (void)swizzle:(id)targetInstance instanceSelector:(SEL)originalSelector withInstanceSelector:(SEL)newSelector {
id target = object_getClass(targetInstance);
Method originalMethod = class_getInstanceMethod(target, originalSelector);
Method newMethod = class_getInstanceMethod(target, newSelector);
[self swizzle:target selector:originalSelector newSelector:newSelector originalMethod:originalMethod newMethod:newMethod];
}

+ (void)swizzle:(id)targetClass classSelector:(SEL)originalSelector withClassSelector:(SEL)newSelector {
id target = object_getClass(targetClass);
Method originalMethod = class_getClassMethod(target, originalSelector);
Method newMethod = class_getClassMethod(target, newSelector);
[self swizzle:target selector:originalSelector newSelector:newSelector originalMethod:originalMethod newMethod:newMethod];
}

#pragma mark - Private methods

+ (void)swizzle:(id)target
selector:(SEL)originalSelector
newSelector:(SEL)newSelector
originalMethod:(Method)originalMethod
newMethod:(Method)newMethod {
BOOL methodAdded = class_addMethod(target, originalSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
if (methodAdded) {
class_replaceMethod(target, newSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, newMethod);
}
}

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

#import <Foundation/Foundation.h>

@interface ASHook : NSObject

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

#import "ASHook.h"

@implementation ASHook

@end
2 changes: 2 additions & 0 deletions ASHookIt/ASHookIt.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
//

#import <UIKit/UIKit.h>
#import <ASHookIt/ASHook.h>
#import <ASHookIt/ASHook+MethodSwizzler.h>

//! Project version number for ASHookIt.
FOUNDATION_EXPORT double ASHookItVersionNumber;
Expand Down
17 changes: 17 additions & 0 deletions ASHookItDemo/ASHookItDemo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
remoteGlobalIDString = 6DCD89341E63091F00F620D2;
remoteInfo = ASHookItTests;
};
6DCD89781E6309DA00F620D2 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 6DCD896B1E63098700F620D2 /* ASHookIt.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 6DCD892A1E63091F00F620D2;
remoteInfo = ASHookIt;
};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
Expand Down Expand Up @@ -126,6 +133,7 @@
buildRules = (
);
dependencies = (
6DCD89791E6309DA00F620D2 /* PBXTargetDependency */,
);
name = ASHookItDemo;
productName = ASHookItDemo;
Expand Down Expand Up @@ -215,6 +223,14 @@
};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
6DCD89791E6309DA00F620D2 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = ASHookIt;
targetProxy = 6DCD89781E6309DA00F620D2 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */

/* Begin PBXVariantGroup section */
6DCD895D1E63097200F620D2 /* Main.storyboard */ = {
isa = PBXVariantGroup;
Expand Down Expand Up @@ -366,6 +382,7 @@
6DCD896A1E63097200F620D2 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
1 change: 0 additions & 1 deletion ASHookItDemo/ASHookItDemo/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@

@property (strong, nonatomic) UIWindow *window;


@end

29 changes: 0 additions & 29 deletions ASHookItDemo/ASHookItDemo/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,9 @@ @interface AppDelegate ()

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}


- (void)applicationWillResignActive:(UIApplication *)application {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}


- (void)applicationDidEnterBackground:(UIApplication *)application {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}


- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}


- (void)applicationDidBecomeActive:(UIApplication *)application {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}


- (void)applicationWillTerminate:(UIApplication *)application {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}


@end
18 changes: 13 additions & 5 deletions ASHookItDemo/ASHookItDemo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11134" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="49e-Tb-3d3">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11762" systemVersion="16D32" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="49e-Tb-3d3">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11106"/>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11757"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--First-->
<scene sceneID="hNz-n2-bh7">
<objects>
<viewController id="9pv-A4-QxB" customClass="FirstViewController" customModuleProvider="" sceneMemberID="viewController">
<viewController id="9pv-A4-QxB" customClass="FirstViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ia1-K6-d13"/>
<viewControllerLayoutGuide type="bottom" id="4ug-Mw-9AY"/>
Expand All @@ -18,12 +22,14 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="First View" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="KQZ-1w-vlD">
<rect key="frame" x="109" y="313" width="157.5" height="41.5"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loaded by FirstViewController" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="A5M-7J-77L">
<rect key="frame" x="90.5" y="362.5" width="194.5" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
Expand All @@ -46,7 +52,7 @@
<!--Second-->
<scene sceneID="wg7-f3-ORb">
<objects>
<viewController id="8rJ-Kc-sve" customClass="SecondViewController" customModuleProvider="" sceneMemberID="viewController">
<viewController id="8rJ-Kc-sve" customClass="SecondViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="L7p-HK-0SC"/>
<viewControllerLayoutGuide type="bottom" id="Djb-ko-YwX"/>
Expand All @@ -56,12 +62,14 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Second View" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="zEq-FU-wV5">
<rect key="frame" x="83" y="313" width="209.5" height="41.5"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" name="Helvetica" family="Helvetica" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loaded by SecondViewController" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NDk-cv-Gan">
<rect key="frame" x="80" y="362.5" width="215" height="17"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
Expand Down
30 changes: 25 additions & 5 deletions ASHookItDemo/ASHookItDemo/FirstViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,43 @@
//

#import "FirstViewController.h"
#import <ASHookIt/ASHookIt.h>

@interface FirstViewController ()

@end

@implementation FirstViewController

#pragma mark - Swizzling example 1

+ (void)initialize {
// A simple class method swizzling example.
[ASHook swizzle:self classSelector:@selector(alloc) withClassSelector:@selector(swizzledAlloc)];
}

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

#pragma mark - Swizzling example 2

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

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

[self logHelloWorld];
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
- (void)logGoodbyeWorld {
NSLog(@"Hello World!");
}

- (void)logHelloWorld {
NSLog(@"Goodbye World!");
}

@end

0 comments on commit 2895c7c

Please sign in to comment.