├── .gitignore ├── LICENSE ├── README.md ├── ZKSwizzle.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── Alex.xcuserdatad │ └── xcschemes │ ├── ZKSwizzle.xcscheme │ ├── iOSTests.xcscheme │ └── xcschememanagement.plist ├── ZKSwizzle ├── ZKSwizzle.h └── ZKSwizzle.m ├── ZKSwizzleTests ├── Info.plist └── ZKTests.m └── iOSTests ├── Info.plist └── iOSTests.m /.gitignore: -------------------------------------------------------------------------------- 1 | # CocoaPods 2 | # 3 | # We recommend against adding the Pods directory to your .gitignore. However 4 | # you should judge for yourself, the pros and cons are mentioned at: 5 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? 6 | # 7 | # Pods/ 8 | 9 | .DS_Store 10 | ZKSwizzle.xcodeproj/project.xcworkspace/xcuserdata/Alex.xcuserdatad/UserInterfaceState.xcuserstate 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Alex Zielenski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ZKSwizzle 2 | ========= 3 | 4 | Streamlined way to swizzle Objective-C instance and class methods. `ZKSwizzle` makes swizzling instance and class methods of an Objective-C class as declarative as possible. You define a new class and implement all the methods you want to swizzle/add and then call `[ZKSwizzle swizzleClass: forClass:]` and any calls to the target class will be instead routed to your own. 5 | 6 | ZKSwizzle also provides macros for calling the original implementation if need be and for calling the implementation of the superclass of the swizzled class. Enough talk, let's get crackin': 7 | 8 | 9 | ```objc 10 | @interface OriginalObject : NSObject 11 | @end 12 | 13 | // Define a class which we will swizzle 14 | @implementation OriginalObject 15 | + (BOOL)isSubclassOfClass:(Class)aClass { return YES; } 16 | + (NSString *)classMethod { return @"original"; } 17 | + (NSString *)description { return @"original"; } 18 | - (NSString *)instanceMethod { return @"original"; } 19 | - (NSString *)description { return @"original"; } 20 | @end 21 | 22 | // All methods on this class which are present on the class that 23 | // it is swizzled to (including superclasses) are called instead of their 24 | // original implementation. The original implementaion can be accessed with the 25 | // _orig(TYPE, ...) macro and the implementation of the superclass of the class which 26 | // it was swizzled to can be access with the _super(TYPE, ...) macro 27 | // hook(TargetClass) defines a class for 28 | // you that will get swizzled automatically on launch with the TargetClass 29 | hook(OriginalObject) 30 | // Returns YES 31 | + (BOOL)isSubclassOfClass:(Class)aClass { return _orig(BOOL); } 32 | 33 | // Returns "original_replaced" 34 | - (NSString *)className { return [_orig(NSString *) stringByAppendingString:@"_replaced"]; } 35 | 36 | // Returns "replaced" when called on the OriginalObject class 37 | + (NSString *)classMethod { return @"replaced"; } 38 | 39 | // Returns the default description implemented by NSObject 40 | + (NSString *)description { return _super(NSString *); } 41 | 42 | // Returns "replaced" when called on an instance of OriginalObject 43 | - (NSString *)instanceMethod { return @"replaced"; } 44 | 45 | // Returns the default description implemented by NSObject 46 | - (NSString *)description { return _super(NSString *); } 47 | 48 | // This method is added to instances of OriginalObject and can be called 49 | // like any normal function on OriginalObject 50 | - (void)addedMethod { NSLog(@"this method was added to OriginalObject"); } 51 | endhook 52 | ``` 53 | 54 | 55 | Call this somewhere to initialize the swizzling: 56 | ```objc 57 | // ZKSwizzle(SOURCE, DST) is a macro shorthand for calling 58 | // ZKSwizzleInterface handles this step for you, but you will 59 | // have to call it manually if you don't use ZKSwizzleInterface 60 | +swizzleClass:forClass: on ZKSwizzle 61 | ZKSwizzle(ReplacementObject, OriginalObject); 62 | ``` 63 | 64 | ZKSwizzle also has macros in place for hooking instance variables: 65 | ```objc 66 | // gets the value of _myIvar on self 67 | int myIvar = ZKHookIvar(self, int, "_myIvar"); 68 | 69 | // gets the pointer to _myIvar on self so you can reassign it 70 | int *myIvar = &ZKHookIvar(self, int, "_myIvar"); 71 | // set the value of myIvar on the object 72 | *myIvar = 3; 73 | ``` 74 | 75 | You can also have grouped hooks, which means you can swizzle a specific class differently depending on something specific: 76 | ```objc 77 | @interface GroupClass : NSObject 78 | + (NSString *)classMethod; 79 | - (NSString *)instanceMethod; 80 | @end 81 | 82 | @implementation GroupClass 83 | + (NSString *)classMethod { return @"classMethod"; } 84 | - (NSString *)instanceMethod { return @"instanceMethod"; } 85 | @end 86 | 87 | hook(GroupClass, Yosemite) 88 | + (NSString *)classMethod { return @"swizzled"; } 89 | - (NSString *)instanceMethod { return @"swizzled"; } 90 | endhook 91 | 92 | hook(GroupClass, Mavericks) 93 | + (NSString *)classMethod { return @"swizzled2"; } 94 | - (NSString *)instanceMethod { return @"swizzled2"; } 95 | endhook 96 | 97 | ctor { 98 | int ver = 1; 99 | ver == 1 ? ZKSwizzleGroup(Yosemite) : ZKSwizzleGroup(Mavericks); 100 | } 101 | ``` 102 | 103 | # "Swizzling the right way" 104 | 105 | Some say that using `method_exchangeImplementations` causes problems with the original implementation being passed a replaced `_cmd` such as `old_description` which would be the new selector for the original implementation of a swizzled `description`. ZKSwizzle solves this problem with `ZKOrig(TYPE, ...)` which passes the correct selector to the original implementation and thus avoids this problem. 106 | 107 | #License 108 | 109 | ZKSwizzle is available on the permissive [MIT License](http://opensource.org/licenses/mit-license.php) 110 | 111 | -------------------------------------------------------------------------------- /ZKSwizzle.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | FA3527851980EB8300EF2D5B /* ZKSwizzle.h in Headers */ = {isa = PBXBuildFile; fileRef = FA3527841980EB8300EF2D5B /* ZKSwizzle.h */; }; 11 | FA3527871980EB8300EF2D5B /* ZKSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3527861980EB8300EF2D5B /* ZKSwizzle.m */; }; 12 | FA35278D1980EB8300EF2D5B /* libZKSwizzle.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA3527811980EB8300EF2D5B /* libZKSwizzle.a */; }; 13 | FA35279C1980EC8C00EF2D5B /* ZKTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA35279B1980EC8C00EF2D5B /* ZKTests.m */; }; 14 | FA700E251AFFD8AC00C19257 /* iOSTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FA700E241AFFD8AC00C19257 /* iOSTests.m */; }; 15 | FA700E2C1AFFD91600C19257 /* ZKSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = FA3527861980EB8300EF2D5B /* ZKSwizzle.m */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | FA35278E1980EB8300EF2D5B /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = FA3527791980EB8300EF2D5B /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = FA3527801980EB8300EF2D5B; 24 | remoteInfo = ZKSwizzle; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | FA3527811980EB8300EF2D5B /* libZKSwizzle.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libZKSwizzle.a; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | FA3527841980EB8300EF2D5B /* ZKSwizzle.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ZKSwizzle.h; sourceTree = ""; }; 31 | FA3527861980EB8300EF2D5B /* ZKSwizzle.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ZKSwizzle.m; sourceTree = ""; }; 32 | FA35278C1980EB8300EF2D5B /* ZKSwizzleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ZKSwizzleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 33 | FA3527921980EB8300EF2D5B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 34 | FA35279B1980EC8C00EF2D5B /* ZKTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ZKTests.m; sourceTree = ""; }; 35 | FA37F5B51CB388AB00F23F09 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 36 | FA700E201AFFD8AC00C19257 /* iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iOSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | FA700E231AFFD8AC00C19257 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 38 | FA700E241AFFD8AC00C19257 /* iOSTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iOSTests.m; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | FA35277E1980EB8300EF2D5B /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | ); 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | FA3527891980EB8300EF2D5B /* Frameworks */ = { 50 | isa = PBXFrameworksBuildPhase; 51 | buildActionMask = 2147483647; 52 | files = ( 53 | FA35278D1980EB8300EF2D5B /* libZKSwizzle.a in Frameworks */, 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | FA700E1D1AFFD8AC00C19257 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | FA3527781980EB8300EF2D5B = { 68 | isa = PBXGroup; 69 | children = ( 70 | FA37F5B51CB388AB00F23F09 /* Cocoa.framework */, 71 | FA3527831980EB8300EF2D5B /* ZKSwizzle */, 72 | FA3527901980EB8300EF2D5B /* ZKSwizzleTests */, 73 | FA700E211AFFD8AC00C19257 /* iOSTests */, 74 | FA3527821980EB8300EF2D5B /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | FA3527821980EB8300EF2D5B /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | FA3527811980EB8300EF2D5B /* libZKSwizzle.a */, 82 | FA35278C1980EB8300EF2D5B /* ZKSwizzleTests.xctest */, 83 | FA700E201AFFD8AC00C19257 /* iOSTests.xctest */, 84 | ); 85 | name = Products; 86 | sourceTree = ""; 87 | }; 88 | FA3527831980EB8300EF2D5B /* ZKSwizzle */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | FA3527841980EB8300EF2D5B /* ZKSwizzle.h */, 92 | FA3527861980EB8300EF2D5B /* ZKSwizzle.m */, 93 | ); 94 | path = ZKSwizzle; 95 | sourceTree = ""; 96 | }; 97 | FA3527901980EB8300EF2D5B /* ZKSwizzleTests */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | FA35279B1980EC8C00EF2D5B /* ZKTests.m */, 101 | FA3527911980EB8300EF2D5B /* Supporting Files */, 102 | ); 103 | path = ZKSwizzleTests; 104 | sourceTree = ""; 105 | }; 106 | FA3527911980EB8300EF2D5B /* Supporting Files */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | FA3527921980EB8300EF2D5B /* Info.plist */, 110 | ); 111 | name = "Supporting Files"; 112 | sourceTree = ""; 113 | }; 114 | FA700E211AFFD8AC00C19257 /* iOSTests */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | FA700E241AFFD8AC00C19257 /* iOSTests.m */, 118 | FA700E221AFFD8AC00C19257 /* Supporting Files */, 119 | ); 120 | path = iOSTests; 121 | sourceTree = ""; 122 | }; 123 | FA700E221AFFD8AC00C19257 /* Supporting Files */ = { 124 | isa = PBXGroup; 125 | children = ( 126 | FA700E231AFFD8AC00C19257 /* Info.plist */, 127 | ); 128 | name = "Supporting Files"; 129 | sourceTree = ""; 130 | }; 131 | /* End PBXGroup section */ 132 | 133 | /* Begin PBXHeadersBuildPhase section */ 134 | FA35277F1980EB8300EF2D5B /* Headers */ = { 135 | isa = PBXHeadersBuildPhase; 136 | buildActionMask = 2147483647; 137 | files = ( 138 | FA3527851980EB8300EF2D5B /* ZKSwizzle.h in Headers */, 139 | ); 140 | runOnlyForDeploymentPostprocessing = 0; 141 | }; 142 | /* End PBXHeadersBuildPhase section */ 143 | 144 | /* Begin PBXNativeTarget section */ 145 | FA3527801980EB8300EF2D5B /* ZKSwizzle */ = { 146 | isa = PBXNativeTarget; 147 | buildConfigurationList = FA3527951980EB8300EF2D5B /* Build configuration list for PBXNativeTarget "ZKSwizzle" */; 148 | buildPhases = ( 149 | FA35277D1980EB8300EF2D5B /* Sources */, 150 | FA35277E1980EB8300EF2D5B /* Frameworks */, 151 | FA35277F1980EB8300EF2D5B /* Headers */, 152 | ); 153 | buildRules = ( 154 | ); 155 | dependencies = ( 156 | ); 157 | name = ZKSwizzle; 158 | productName = ZKSwizzle; 159 | productReference = FA3527811980EB8300EF2D5B /* libZKSwizzle.a */; 160 | productType = "com.apple.product-type.library.static"; 161 | }; 162 | FA35278B1980EB8300EF2D5B /* ZKSwizzleTests */ = { 163 | isa = PBXNativeTarget; 164 | buildConfigurationList = FA3527981980EB8300EF2D5B /* Build configuration list for PBXNativeTarget "ZKSwizzleTests" */; 165 | buildPhases = ( 166 | FA3527881980EB8300EF2D5B /* Sources */, 167 | FA3527891980EB8300EF2D5B /* Frameworks */, 168 | FA35278A1980EB8300EF2D5B /* Resources */, 169 | ); 170 | buildRules = ( 171 | ); 172 | dependencies = ( 173 | FA35278F1980EB8300EF2D5B /* PBXTargetDependency */, 174 | ); 175 | name = ZKSwizzleTests; 176 | productName = ZKSwizzleTests; 177 | productReference = FA35278C1980EB8300EF2D5B /* ZKSwizzleTests.xctest */; 178 | productType = "com.apple.product-type.bundle.unit-test"; 179 | }; 180 | FA700E1F1AFFD8AC00C19257 /* iOSTests */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = FA700E291AFFD8AC00C19257 /* Build configuration list for PBXNativeTarget "iOSTests" */; 183 | buildPhases = ( 184 | FA700E1C1AFFD8AC00C19257 /* Sources */, 185 | FA700E1D1AFFD8AC00C19257 /* Frameworks */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = iOSTests; 192 | productName = iOSTests; 193 | productReference = FA700E201AFFD8AC00C19257 /* iOSTests.xctest */; 194 | productType = "com.apple.product-type.bundle.unit-test"; 195 | }; 196 | /* End PBXNativeTarget section */ 197 | 198 | /* Begin PBXProject section */ 199 | FA3527791980EB8300EF2D5B /* Project object */ = { 200 | isa = PBXProject; 201 | attributes = { 202 | LastUpgradeCheck = 0730; 203 | ORGANIZATIONNAME = "Alexander S Zielenski"; 204 | TargetAttributes = { 205 | FA3527801980EB8300EF2D5B = { 206 | CreatedOnToolsVersion = 6.0; 207 | }; 208 | FA35278B1980EB8300EF2D5B = { 209 | CreatedOnToolsVersion = 6.0; 210 | TestTargetID = FA3527801980EB8300EF2D5B; 211 | }; 212 | FA700E1F1AFFD8AC00C19257 = { 213 | CreatedOnToolsVersion = 6.3; 214 | }; 215 | }; 216 | }; 217 | buildConfigurationList = FA35277C1980EB8300EF2D5B /* Build configuration list for PBXProject "ZKSwizzle" */; 218 | compatibilityVersion = "Xcode 3.2"; 219 | developmentRegion = English; 220 | hasScannedForEncodings = 0; 221 | knownRegions = ( 222 | en, 223 | ); 224 | mainGroup = FA3527781980EB8300EF2D5B; 225 | productRefGroup = FA3527821980EB8300EF2D5B /* Products */; 226 | projectDirPath = ""; 227 | projectRoot = ""; 228 | targets = ( 229 | FA3527801980EB8300EF2D5B /* ZKSwizzle */, 230 | FA35278B1980EB8300EF2D5B /* ZKSwizzleTests */, 231 | FA700E1F1AFFD8AC00C19257 /* iOSTests */, 232 | ); 233 | }; 234 | /* End PBXProject section */ 235 | 236 | /* Begin PBXResourcesBuildPhase section */ 237 | FA35278A1980EB8300EF2D5B /* Resources */ = { 238 | isa = PBXResourcesBuildPhase; 239 | buildActionMask = 2147483647; 240 | files = ( 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXResourcesBuildPhase section */ 245 | 246 | /* Begin PBXSourcesBuildPhase section */ 247 | FA35277D1980EB8300EF2D5B /* Sources */ = { 248 | isa = PBXSourcesBuildPhase; 249 | buildActionMask = 2147483647; 250 | files = ( 251 | FA3527871980EB8300EF2D5B /* ZKSwizzle.m in Sources */, 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | FA3527881980EB8300EF2D5B /* Sources */ = { 256 | isa = PBXSourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | FA35279C1980EC8C00EF2D5B /* ZKTests.m in Sources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | FA700E1C1AFFD8AC00C19257 /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | FA700E251AFFD8AC00C19257 /* iOSTests.m in Sources */, 268 | FA700E2C1AFFD91600C19257 /* ZKSwizzle.m in Sources */, 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | }; 272 | /* End PBXSourcesBuildPhase section */ 273 | 274 | /* Begin PBXTargetDependency section */ 275 | FA35278F1980EB8300EF2D5B /* PBXTargetDependency */ = { 276 | isa = PBXTargetDependency; 277 | target = FA3527801980EB8300EF2D5B /* ZKSwizzle */; 278 | targetProxy = FA35278E1980EB8300EF2D5B /* PBXContainerItemProxy */; 279 | }; 280 | /* End PBXTargetDependency section */ 281 | 282 | /* Begin XCBuildConfiguration section */ 283 | FA3527931980EB8300EF2D5B /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | ALWAYS_SEARCH_USER_PATHS = NO; 287 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 288 | CLANG_CXX_LIBRARY = "libc++"; 289 | CLANG_ENABLE_MODULES = YES; 290 | CLANG_ENABLE_OBJC_ARC = NO; 291 | CLANG_WARN_BOOL_CONVERSION = YES; 292 | CLANG_WARN_CONSTANT_CONVERSION = YES; 293 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 294 | CLANG_WARN_EMPTY_BODY = YES; 295 | CLANG_WARN_ENUM_CONVERSION = YES; 296 | CLANG_WARN_INT_CONVERSION = YES; 297 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 298 | CLANG_WARN_UNREACHABLE_CODE = YES; 299 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 300 | COPY_PHASE_STRIP = NO; 301 | ENABLE_STRICT_OBJC_MSGSEND = YES; 302 | ENABLE_TESTABILITY = YES; 303 | GCC_C_LANGUAGE_STANDARD = gnu99; 304 | GCC_DYNAMIC_NO_PIC = NO; 305 | GCC_OPTIMIZATION_LEVEL = 0; 306 | GCC_PREPROCESSOR_DEFINITIONS = ( 307 | "DEBUG=1", 308 | "$(inherited)", 309 | ); 310 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 311 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 312 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 313 | GCC_WARN_UNDECLARED_SELECTOR = YES; 314 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 315 | GCC_WARN_UNUSED_FUNCTION = YES; 316 | GCC_WARN_UNUSED_VARIABLE = YES; 317 | MACOSX_DEPLOYMENT_TARGET = 10.9; 318 | MTL_ENABLE_DEBUG_INFO = YES; 319 | ONLY_ACTIVE_ARCH = YES; 320 | SDKROOT = macosx; 321 | }; 322 | name = Debug; 323 | }; 324 | FA3527941980EB8300EF2D5B /* Release */ = { 325 | isa = XCBuildConfiguration; 326 | buildSettings = { 327 | ALWAYS_SEARCH_USER_PATHS = NO; 328 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 329 | CLANG_CXX_LIBRARY = "libc++"; 330 | CLANG_ENABLE_MODULES = YES; 331 | CLANG_ENABLE_OBJC_ARC = NO; 332 | CLANG_WARN_BOOL_CONVERSION = YES; 333 | CLANG_WARN_CONSTANT_CONVERSION = YES; 334 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 335 | CLANG_WARN_EMPTY_BODY = YES; 336 | CLANG_WARN_ENUM_CONVERSION = YES; 337 | CLANG_WARN_INT_CONVERSION = YES; 338 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 339 | CLANG_WARN_UNREACHABLE_CODE = YES; 340 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 341 | COPY_PHASE_STRIP = YES; 342 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 343 | ENABLE_NS_ASSERTIONS = NO; 344 | ENABLE_STRICT_OBJC_MSGSEND = YES; 345 | GCC_C_LANGUAGE_STANDARD = gnu99; 346 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | MACOSX_DEPLOYMENT_TARGET = 10.9; 353 | MTL_ENABLE_DEBUG_INFO = NO; 354 | SDKROOT = macosx; 355 | }; 356 | name = Release; 357 | }; 358 | FA3527961980EB8300EF2D5B /* Debug */ = { 359 | isa = XCBuildConfiguration; 360 | buildSettings = { 361 | COMBINE_HIDPI_IMAGES = YES; 362 | EXECUTABLE_PREFIX = lib; 363 | PRODUCT_NAME = "$(TARGET_NAME)"; 364 | }; 365 | name = Debug; 366 | }; 367 | FA3527971980EB8300EF2D5B /* Release */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | COMBINE_HIDPI_IMAGES = YES; 371 | EXECUTABLE_PREFIX = lib; 372 | PRODUCT_NAME = "$(TARGET_NAME)"; 373 | }; 374 | name = Release; 375 | }; 376 | FA3527991980EB8300EF2D5B /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | CLANG_ENABLE_MODULES = NO; 380 | COMBINE_HIDPI_IMAGES = YES; 381 | FRAMEWORK_SEARCH_PATHS = ( 382 | "$(DEVELOPER_FRAMEWORKS_DIR)", 383 | "$(inherited)", 384 | ); 385 | GCC_PREPROCESSOR_DEFINITIONS = ( 386 | "DEBUG=1", 387 | "$(inherited)", 388 | ); 389 | INFOPLIST_FILE = ZKSwizzleTests/Info.plist; 390 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 391 | PRODUCT_BUNDLE_IDENTIFIER = "com.alexzielenski.${PRODUCT_NAME:rfc1034identifier}"; 392 | PRODUCT_NAME = "$(TARGET_NAME)"; 393 | }; 394 | name = Debug; 395 | }; 396 | FA35279A1980EB8300EF2D5B /* Release */ = { 397 | isa = XCBuildConfiguration; 398 | buildSettings = { 399 | CLANG_ENABLE_MODULES = NO; 400 | COMBINE_HIDPI_IMAGES = YES; 401 | FRAMEWORK_SEARCH_PATHS = ( 402 | "$(DEVELOPER_FRAMEWORKS_DIR)", 403 | "$(inherited)", 404 | ); 405 | INFOPLIST_FILE = ZKSwizzleTests/Info.plist; 406 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 407 | PRODUCT_BUNDLE_IDENTIFIER = "com.alexzielenski.${PRODUCT_NAME:rfc1034identifier}"; 408 | PRODUCT_NAME = "$(TARGET_NAME)"; 409 | }; 410 | name = Release; 411 | }; 412 | FA700E2A1AFFD8AC00C19257 /* Debug */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | CLANG_ENABLE_MODULES = NO; 416 | CODE_SIGN_IDENTITY = "iPhone Developer: Alexander Zielenski (M4FR9448Q7)"; 417 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 418 | FRAMEWORK_SEARCH_PATHS = ( 419 | "$(SDKROOT)/Developer/Library/Frameworks", 420 | "$(inherited)", 421 | ); 422 | GCC_NO_COMMON_BLOCKS = YES; 423 | GCC_PREPROCESSOR_DEFINITIONS = ( 424 | "DEBUG=1", 425 | "$(inherited)", 426 | ); 427 | INFOPLIST_FILE = iOSTests/Info.plist; 428 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 429 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 430 | PRODUCT_BUNDLE_IDENTIFIER = "com.alexzielenski.$(PRODUCT_NAME:rfc1034identifier)"; 431 | PRODUCT_NAME = "$(TARGET_NAME)"; 432 | SDKROOT = iphoneos; 433 | VALIDATE_PRODUCT = YES; 434 | }; 435 | name = Debug; 436 | }; 437 | FA700E2B1AFFD8AC00C19257 /* Release */ = { 438 | isa = XCBuildConfiguration; 439 | buildSettings = { 440 | CLANG_ENABLE_MODULES = NO; 441 | CODE_SIGN_IDENTITY = "iPhone Developer: Alexander Zielenski (M4FR9448Q7)"; 442 | COPY_PHASE_STRIP = NO; 443 | FRAMEWORK_SEARCH_PATHS = ( 444 | "$(SDKROOT)/Developer/Library/Frameworks", 445 | "$(inherited)", 446 | ); 447 | GCC_NO_COMMON_BLOCKS = YES; 448 | INFOPLIST_FILE = iOSTests/Info.plist; 449 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 451 | PRODUCT_BUNDLE_IDENTIFIER = "com.alexzielenski.$(PRODUCT_NAME:rfc1034identifier)"; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SDKROOT = iphoneos; 454 | VALIDATE_PRODUCT = YES; 455 | }; 456 | name = Release; 457 | }; 458 | /* End XCBuildConfiguration section */ 459 | 460 | /* Begin XCConfigurationList section */ 461 | FA35277C1980EB8300EF2D5B /* Build configuration list for PBXProject "ZKSwizzle" */ = { 462 | isa = XCConfigurationList; 463 | buildConfigurations = ( 464 | FA3527931980EB8300EF2D5B /* Debug */, 465 | FA3527941980EB8300EF2D5B /* Release */, 466 | ); 467 | defaultConfigurationIsVisible = 0; 468 | defaultConfigurationName = Release; 469 | }; 470 | FA3527951980EB8300EF2D5B /* Build configuration list for PBXNativeTarget "ZKSwizzle" */ = { 471 | isa = XCConfigurationList; 472 | buildConfigurations = ( 473 | FA3527961980EB8300EF2D5B /* Debug */, 474 | FA3527971980EB8300EF2D5B /* Release */, 475 | ); 476 | defaultConfigurationIsVisible = 0; 477 | defaultConfigurationName = Release; 478 | }; 479 | FA3527981980EB8300EF2D5B /* Build configuration list for PBXNativeTarget "ZKSwizzleTests" */ = { 480 | isa = XCConfigurationList; 481 | buildConfigurations = ( 482 | FA3527991980EB8300EF2D5B /* Debug */, 483 | FA35279A1980EB8300EF2D5B /* Release */, 484 | ); 485 | defaultConfigurationIsVisible = 0; 486 | defaultConfigurationName = Release; 487 | }; 488 | FA700E291AFFD8AC00C19257 /* Build configuration list for PBXNativeTarget "iOSTests" */ = { 489 | isa = XCConfigurationList; 490 | buildConfigurations = ( 491 | FA700E2A1AFFD8AC00C19257 /* Debug */, 492 | FA700E2B1AFFD8AC00C19257 /* Release */, 493 | ); 494 | defaultConfigurationIsVisible = 0; 495 | defaultConfigurationName = Release; 496 | }; 497 | /* End XCConfigurationList section */ 498 | }; 499 | rootObject = FA3527791980EB8300EF2D5B /* Project object */; 500 | } 501 | -------------------------------------------------------------------------------- /ZKSwizzle.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ZKSwizzle.xcodeproj/xcuserdata/Alex.xcuserdatad/xcschemes/ZKSwizzle.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /ZKSwizzle.xcodeproj/xcuserdata/Alex.xcuserdatad/xcschemes/iOSTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 61 | 62 | 68 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | 86 | 87 | 88 | 89 | 91 | 92 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /ZKSwizzle.xcodeproj/xcuserdata/Alex.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ZKSwizzle.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | iOSTests.xcscheme 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | FA171CD21AF4624A007B40AF 21 | 22 | primary 23 | 24 | 25 | FA171CE61AF4624A007B40AF 26 | 27 | primary 28 | 29 | 30 | FA171CF81AF46266007B40AF 31 | 32 | primary 33 | 34 | 35 | FA171D101AF46266007B40AF 36 | 37 | primary 38 | 39 | 40 | FA3527801980EB8300EF2D5B 41 | 42 | primary 43 | 44 | 45 | FA35278B1980EB8300EF2D5B 46 | 47 | primary 48 | 49 | 50 | FA700E0F1AFFD89F00C19257 51 | 52 | primary 53 | 54 | 55 | FA700E1F1AFFD8AC00C19257 56 | 57 | primary 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /ZKSwizzle/ZKSwizzle.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZKSwizzle.h 3 | // ZKSwizzle 4 | // 5 | // Created by Alexander S Zielenski on 7/24/14. 6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | // This is a class for streamlining swizzling. Simply create a new class of any name you want and 14 | // Example: 15 | /* 16 | @interface ZKHookClass : NSObject 17 | - (NSString *)description; // hooks -description on NSObject 18 | - (void)addedMethod; // all subclasses of NSObject now respond to -addedMethod 19 | @end 20 | 21 | @implementation ZKHookClass 22 | ... 23 | @end 24 | 25 | [ZKSwizzle swizzleClass:ZKClass(ZKHookClass) forClass:ZKClass(destination)]; 26 | */ 27 | 28 | #ifndef ZKSWIZZLE_DEFS 29 | #define ZKSWIZZLE_DEFS 30 | 31 | // CRAZY MACROS FOR DYNAMIC PROTOTYPE CREATION 32 | #define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5 ,4 ,3 ,2, 1, 0) 33 | #define VA_NUM_ARGS_IMPL(_0, _1,_2,_3,_4,_5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20 ,N,...) N 34 | 35 | #define WRAP0() 36 | #define WRAP1(VARIABLE) , typeof ( VARIABLE ) 37 | #define WRAP2(VARIABLE, ...) WRAP1(VARIABLE) WRAP1(__VA_ARGS__) 38 | #define WRAP3(VARIABLE, ...) WRAP1(VARIABLE) WRAP2(__VA_ARGS__) 39 | #define WRAP4(VARIABLE, ...) WRAP1(VARIABLE) WRAP3(__VA_ARGS__) 40 | #define WRAP5(VARIABLE, ...) WRAP1(VARIABLE) WRAP4(__VA_ARGS__) 41 | #define WRAP6(VARIABLE, ...) WRAP1(VARIABLE) WRAP5(__VA_ARGS__) 42 | #define WRAP7(VARIABLE, ...) WRAP1(VARIABLE) WRAP6(__VA_ARGS__) 43 | #define WRAP8(VARIABLE, ...) WRAP1(VARIABLE) WRAP7(__VA_ARGS__) 44 | #define WRAP9(VARIABLE, ...) WRAP1(VARIABLE) WRAP8(__VA_ARGS__) 45 | #define WRAP10(VARIABLE, ...) WRAP1(VARIABLE) WRAP9(__VA_ARGS__) 46 | #define WRAP11(VARIABLE, ...) WRAP1(VARIABLE) WRAP10(__VA_ARGS__) 47 | #define WRAP12(VARIABLE, ...) WRAP1(VARIABLE) WRAP11(__VA_ARGS__) 48 | #define WRAP13(VARIABLE, ...) WRAP1(VARIABLE) WRAP12(__VA_ARGS__) 49 | #define WRAP14(VARIABLE, ...) WRAP1(VARIABLE) WRAP13(__VA_ARGS__) 50 | #define WRAP15(VARIABLE, ...) WRAP1(VARIABLE) WRAP14(__VA_ARGS__) 51 | #define WRAP16(VARIABLE, ...) WRAP1(VARIABLE) WRAP15(__VA_ARGS__) 52 | #define WRAP17(VARIABLE, ...) WRAP1(VARIABLE) WRAP16(__VA_ARGS__) 53 | #define WRAP18(VARIABLE, ...) WRAP1(VARIABLE) WRAP17(__VA_ARGS__) 54 | #define WRAP19(VARIABLE, ...) WRAP1(VARIABLE) WRAP18(__VA_ARGS__) 55 | #define WRAP20(VARIABLE, ...) WRAP1(VARIABLE) WRAP19(__VA_ARGS__) 56 | 57 | #define CAT(A, B) A ## B 58 | #define INVOKE(MACRO, NUMBER, ...) CAT(MACRO, NUMBER)(__VA_ARGS__) 59 | #define WRAP_LIST(...) INVOKE(WRAP, VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__) 60 | 61 | // Gets the a class with the name CLASS 62 | #define ZKClass(CLASS) objc_getClass(#CLASS) 63 | 64 | // returns the value of an instance variable. 65 | #if !__has_feature(objc_arc) 66 | #define ZKHookIvar(OBJECT, TYPE, NAME) (*(TYPE *)ZKIvarPointer(OBJECT, NAME)) 67 | #else 68 | #define ZKHookIvar(OBJECT, TYPE, NAME) \ 69 | _Pragma("clang diagnostic push") \ 70 | _Pragma("clang diagnostic ignored \"-Wignored-attributes\"") \ 71 | (*(__unsafe_unretained TYPE *)ZKIvarPointer(OBJECT, NAME)) \ 72 | _Pragma("clang diagnostic pop") 73 | #endif 74 | 75 | //////////////////////////////////////////////////////////////////////////////// 76 | //// Core Macros (For fine-tuned Use) 77 | //////////////////////////////////////////////////////////////////////////////// 78 | // returns the original implementation of the swizzled function or null or not found 79 | #define ZKOrig(TYPE, ...) ((TYPE (*)(id, SEL WRAP_LIST(__VA_ARGS__)))(ZKOriginalImplementation(self, _cmd, __PRETTY_FUNCTION__)))(self, _cmd, ##__VA_ARGS__) 80 | 81 | // returns the original implementation of the superclass of the object swizzled 82 | #define ZKSuper(TYPE, ...) ((TYPE (*)(id, SEL WRAP_LIST(__VA_ARGS__)))(ZKSuperImplementation(self, _cmd, __PRETTY_FUNCTION__)))(self, _cmd, ##__VA_ARGS__) 83 | 84 | #define _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPERCLASS, GROUP, IMMEDIATELY) \ 85 | @interface _$ ## CLASS_NAME : SUPERCLASS @end\ 86 | @implementation _$ ## CLASS_NAME\ 87 | + (void)initialize {}\ 88 | @end\ 89 | @interface CLASS_NAME : _$ ## CLASS_NAME @end\ 90 | @implementation CLASS_NAME (ZKSWIZZLE)\ 91 | + (void)load {\ 92 | if (IMMEDIATELY) {\ 93 | [self _ZK_unconditionallySwizzle];\ 94 | } else {\ 95 | _$ZKRegisterInterface(self, #GROUP);\ 96 | }\ 97 | }\ 98 | + (void)_ZK_unconditionallySwizzle {\ 99 | ZKSwizzle(CLASS_NAME, TARGET_CLASS);\ 100 | }\ 101 | @end 102 | 103 | // Bootstraps your swizzling class so that it requires no setup 104 | // outside of this macro call 105 | // If you override +load you must call ZKSwizzle(CLASS_NAME, TARGET_CLASS) 106 | // yourself, otherwise the swizzling would not take place 107 | #define ZKSwizzleInterface(CLASS_NAME, TARGET_CLASS, SUPERCLASS) \ 108 | _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPERCLASS, ZK_UNGROUPED, YES) 109 | 110 | // Same as ZKSwizzleInterface, except 111 | #define ZKSwizzleInterfaceGroup(CLASS_NAME, TARGET_CLASS, SUPER_CLASS, GROUP) \ 112 | _ZKSwizzleInterfaceConditionally(CLASS_NAME, TARGET_CLASS, SUPER_CLASS, GROUP, NO) 113 | 114 | //////////////////////////////////////////////////////////////////////////////// 115 | //// Sugar Macros (For general use) 116 | //////////////////////////////////////////////////////////////////////////////// 117 | // Inspired by logos. Credits to @mstg! 118 | 119 | #define __GEN_CLASS(TARGET, LINE) __ZK_## LINE## TARGET 120 | #define _GEN_CLASS(TARGET, LINE) __GEN_CLASS(TARGET, LINE) 121 | #define GEN_CLASS(TARGET) _GEN_CLASS(TARGET, __LINE__) 122 | 123 | #define hook_2(TARGET, GROUP) \ 124 | ZKSwizzleInterfaceGroup(GEN_CLASS(TARGET), TARGET, NSObject, GROUP) @implementation GEN_CLASS(TARGET) 125 | 126 | #define hook_1(TARGET) \ 127 | ZKSwizzleInterface(GEN_CLASS(TARGET), TARGET, NSObject) @implementation GEN_CLASS(TARGET) 128 | 129 | #define endhook @end 130 | 131 | #define _orig(...) ZKOrig(__VA_ARGS__) 132 | #define _super(...) ZKSuper(__VA_ARGS__) 133 | 134 | #define __HOOK(ARGC, ARGS...) hook_ ## ARGC (ARGS) 135 | #define _HOOK(ARGC, ARGS...) __HOOK(ARGC, ARGS) 136 | #define hook(...) _HOOK(VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__) 137 | #define ctor __attribute__((constructor)) void init() 138 | 139 | #define ZKIgnoreTypes +(BOOL)_ZK_ignoreTypes { return YES; } 140 | 141 | __BEGIN_DECLS 142 | 143 | //////////////////////////////////////////////////////////////////////////////// 144 | //// C Backing (Don't typically call directly) 145 | //////////////////////////////////////////////////////////////////////////////// 146 | 147 | // Make sure to cast this before you use it 148 | typedef id (*ZKIMP)(id, SEL, ...); 149 | 150 | // returns a pointer to the instance variable "name" on the object 151 | void *ZKIvarPointer(id self, const char *name); 152 | // returns the original implementation of a method with selector "sel" of an object hooked by the methods below 153 | ZKIMP ZKOriginalImplementation(id self, SEL sel, const char *info); 154 | // returns the implementation of a method with selector "sel" of the superclass of object 155 | ZKIMP ZKSuperImplementation(id object, SEL sel, const char *info); 156 | 157 | // hooks all the implemented methods of source with destination 158 | // adds any methods that arent implemented on destination to destination that are implemented in source 159 | #define ZKSwizzle(src, dst) _ZKSwizzle(ZKClass(src), ZKClass(dst)) 160 | BOOL _ZKSwizzle(Class src, Class dest); 161 | 162 | #define ZKSwizzleGroup(NAME) _ZKSwizzleGroup(#NAME) 163 | void _$ZKRegisterInterface(Class cls, const char *groupName); 164 | BOOL _ZKSwizzleGroup(const char *groupName); 165 | 166 | // Calls above method with the superclass of source for desination 167 | #define ZKSwizzleClass(src) _ZKSwizzleClass(ZKClass(src)) 168 | BOOL _ZKSwizzleClass(Class cls); 169 | 170 | __END_DECLS 171 | #endif 172 | 173 | -------------------------------------------------------------------------------- /ZKSwizzle/ZKSwizzle.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZKSwizzle.m 3 | // ZKSwizzle 4 | // 5 | // Created by Alexander S Zielenski on 7/24/14. 6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved. 7 | // 8 | 9 | #import "ZKSwizzle.h" 10 | static NSMutableDictionary *classTable; 11 | 12 | @interface NSObject (ZKSwizzle) 13 | + (void)_ZK_unconditionallySwizzle; 14 | + (BOOL)_ZK_ignoreTypes; 15 | @end 16 | 17 | void *ZKIvarPointer(id self, const char *name) { 18 | Ivar ivar = class_getInstanceVariable(object_getClass(self), name); 19 | return ivar == NULL ? NULL : (__bridge void *)self + ivar_getOffset(ivar); 20 | } 21 | 22 | static SEL destinationSelectorForSelector(SEL cmd, Class dst) { 23 | return NSSelectorFromString([@"_ZK_old_" stringByAppendingFormat:@"%s_%@", class_getName(dst), NSStringFromSelector(cmd)]); 24 | } 25 | 26 | static Class classFromInfo(const char *info) { 27 | NSUInteger bracket_index = -1; 28 | for (NSUInteger i = 0; i < strlen(info); i++) { 29 | if (info[i] == '[') { 30 | bracket_index = i; 31 | break; 32 | } 33 | } 34 | bracket_index++; 35 | 36 | if (bracket_index == -1) { 37 | [NSException raise:@"Failed to parse info" format:@"Couldn't find swizzle class for info: %s", info]; 38 | return NULL; 39 | } 40 | 41 | char after_bracket[255]; 42 | memcpy(after_bracket, &info[bracket_index], strlen(info) - bracket_index - 1); 43 | 44 | for (NSUInteger i = 0; i < strlen(info); i++) { 45 | if (after_bracket[i] == ' ') { 46 | after_bracket[i] = '\0'; 47 | } 48 | } 49 | 50 | return objc_getClass(after_bracket); 51 | } 52 | 53 | // takes __PRETTY_FUNCTION__ for info which gives the name of the swizzle source class 54 | /* 55 | 56 | We add the original implementation onto the swizzle class 57 | On ZKOrig, we use __PRETTY_FUNCTION__ to get the name of the swizzle class 58 | Then we get the implementation of that selector on the swizzle class 59 | Then we call it directly, passing in the correct selector and self 60 | 61 | */ 62 | ZKIMP ZKOriginalImplementation(id self, SEL sel, const char *info) { 63 | if (sel == NULL || self == NULL || info == NULL) { 64 | [NSException raise:@"Invalid Arguments" format:@"One of self: %@, self: %@, or info: %s is NULL", self, NSStringFromSelector(sel), info]; 65 | return NULL; 66 | } 67 | 68 | Class cls = classFromInfo(info); 69 | Class dest = object_getClass(self); 70 | 71 | if (cls == NULL || dest == NULL) { 72 | [NSException raise:@"Failed obtain class pair" format:@"src: %@ | dst: %@ | sel: %@", NSStringFromClass(cls), NSStringFromClass(dest), NSStringFromSelector(sel)]; 73 | return NULL; 74 | } 75 | 76 | SEL destSel = destinationSelectorForSelector(sel, cls); 77 | 78 | Method method = class_getInstanceMethod(dest, destSel); 79 | 80 | if (method == NULL) { 81 | if (![NSStringFromClass(cls) isEqualToString:NSStringFromClass([self class])]) { 82 | // There is no implementation at this class level. Call the super implementation 83 | return ZKSuperImplementation(self, sel, info); 84 | } 85 | 86 | [NSException raise:@"Failed to retrieve method" format:@"Got null for the source class %@ with selector %@ (%@)", NSStringFromClass(cls), NSStringFromSelector(sel), NSStringFromSelector(destSel)]; 87 | return NULL; 88 | } 89 | 90 | ZKIMP implementation = (ZKIMP)method_getImplementation(method); 91 | if (implementation == NULL) { 92 | [NSException raise:@"Failed to get implementation" format:@"The objective-c runtime could not get the implementation for %@ on the class %@. There is no fix for this", NSStringFromClass(cls), NSStringFromSelector(sel)]; 93 | } 94 | 95 | return implementation; 96 | } 97 | 98 | ZKIMP ZKSuperImplementation(id object, SEL sel, const char *info) { 99 | if (sel == NULL || object == NULL) { 100 | [NSException raise:@"Invalid Arguments" format:@"One of self: %@, self: %@ is NULL", object, NSStringFromSelector(sel)]; 101 | return NULL; 102 | } 103 | 104 | Class cls = object_getClass(object); 105 | if (cls == NULL) { 106 | [NSException raise:@"Invalid Argument" format:@"Could not obtain class for the passed object"]; 107 | return NULL; 108 | } 109 | 110 | // Two scenarios: 111 | // 1.) The superclass was not swizzled, no problem 112 | // 2.) The superclass was swizzled, problem 113 | 114 | // We want to return the swizzled class's superclass implementation 115 | // If this is a subclass of such a class, we want two behaviors: 116 | // a.) If this imp was also swizzled, no problem, return the superclass's swizzled imp 117 | // b.) This imp was not swizzled, return the class that was originally swizzled's superclass's imp 118 | Class sourceClass = classFromInfo(info); 119 | if (sourceClass != NULL) { 120 | BOOL isClassMethod = class_isMetaClass(cls); 121 | // This was called from a swizzled method, get the class it was swizzled with 122 | NSString *className = classTable[NSStringFromClass(sourceClass)]; 123 | if (className != NULL) { 124 | cls = NSClassFromString(className); 125 | // make sure we get a class method if we asked for one 126 | if (isClassMethod) { 127 | cls = object_getClass(cls); 128 | } 129 | } 130 | } 131 | 132 | cls = class_getSuperclass(cls); 133 | 134 | // This is a root class, it has no super class 135 | if (cls == NULL) { 136 | [NSException raise:@"Invalid Argument" format:@"Could not obtain superclass for the passed object"]; 137 | return NULL; 138 | } 139 | 140 | Method method = class_getInstanceMethod(cls, sel); 141 | if (method == NULL) { 142 | [NSException raise:@"Failed to retrieve method" format:@"We could not find the super implementation for the class %@ and selector %@, are you sure it exists?", NSStringFromClass(cls), NSStringFromSelector(sel)]; 143 | return NULL; 144 | } 145 | 146 | ZKIMP implementation = (ZKIMP)method_getImplementation(method); 147 | if (implementation == NULL) { 148 | [NSException raise:@"Failed to get implementation" format:@"The objective-c runtime could not get the implementation for %@ on the class %@. There is no fix for this", NSStringFromClass(cls), NSStringFromSelector(sel)]; 149 | } 150 | 151 | return implementation; 152 | } 153 | 154 | static BOOL enumerateMethods(Class, Class); 155 | BOOL _ZKSwizzle(Class src, Class dest) { 156 | if (dest == NULL) 157 | return NO; 158 | 159 | NSString *destName = NSStringFromClass(dest); 160 | if (!destName) { 161 | return NO; 162 | } 163 | 164 | if (!classTable) { 165 | classTable = [[NSMutableDictionary alloc] init]; 166 | } 167 | 168 | if ([classTable objectForKey:NSStringFromClass(src)]) { 169 | [NSException raise:@"Invalid Argument" 170 | format:@"This source class (%@) was already swizzled with another, (%@)", NSStringFromClass(src), classTable[NSStringFromClass(src)]]; 171 | return NO; 172 | } 173 | 174 | BOOL success = enumerateMethods(dest, src); 175 | // The above method only gets instance methods. Do the same method for the metaclass of the class 176 | success &= enumerateMethods(object_getClass(dest), object_getClass(src)); 177 | 178 | [classTable setObject:destName forKey:NSStringFromClass(src)]; 179 | return success; 180 | } 181 | 182 | BOOL _ZKSwizzleClass(Class cls) { 183 | return _ZKSwizzle(cls, [cls superclass]); 184 | } 185 | 186 | static BOOL classIgnoresTypes(Class cls) { 187 | if (!class_isMetaClass(cls)) { 188 | cls = object_getClass(cls); 189 | } 190 | 191 | if (class_respondsToSelector(cls, @selector(_ZK_ignoreTypes))) { 192 | Class cls2 = class_createInstance(cls, 0); 193 | return [cls2 _ZK_ignoreTypes]; 194 | } 195 | 196 | return NO; 197 | } 198 | 199 | static BOOL enumerateMethods(Class destination, Class source) { 200 | #if OBJC_API_VERSION < 2 201 | [NSException raise:@"Unsupported feature" format:@"ZKSwizzle is only available in objc 2.0"]; 202 | return NO; 203 | 204 | #else 205 | 206 | unsigned int methodCount; 207 | Method *methodList = class_copyMethodList(source, &methodCount); 208 | BOOL success = YES; 209 | BOOL ignoreTypes = classIgnoresTypes(source); 210 | 211 | for (int i = 0; i < methodCount; i++) { 212 | Method method = methodList[i]; 213 | SEL selector = method_getName(method); 214 | NSString *methodName = NSStringFromSelector(selector); 215 | 216 | // Don't do anything with the unconditional swizzle 217 | if (sel_isEqual(selector, @selector(_ZK_unconditionallySwizzle)) || 218 | sel_isEqual(selector, @selector(_ZK_ignoreTypes))) { 219 | continue; 220 | } 221 | 222 | // We only swizzle methods that are implemented 223 | if (class_respondsToSelector(destination, selector)) { 224 | Method originalMethod = class_getInstanceMethod(destination, selector); 225 | 226 | const char *originalType = method_getTypeEncoding(originalMethod); 227 | const char *newType = method_getTypeEncoding(method); 228 | if (strcmp(originalType, newType) != 0 && !ignoreTypes) { 229 | NSLog(@"ZKSwizzle: incompatible type encoding for %@. (expected %s, got %s)", methodName, originalType, newType); 230 | // Incompatible type encoding 231 | success = NO; 232 | continue; 233 | } 234 | 235 | Method superImp = class_getInstanceMethod(class_getSuperclass(destination), selector); 236 | 237 | if (originalMethod != superImp) { 238 | // We are re-adding the destination selector because it could be on a superclass and not on the class itself. This method could fail 239 | class_addMethod(destination, selector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); 240 | 241 | SEL destSel = destinationSelectorForSelector(selector, source); 242 | if (!class_addMethod(destination, destSel, method_getImplementation(method), method_getTypeEncoding(originalMethod))) { 243 | NSLog(@"ZKSwizzle: failed to add method %@ onto class %@ with selector %@", NSStringFromSelector(selector), NSStringFromClass(source), NSStringFromSelector(destSel)); 244 | success = NO; 245 | continue; 246 | } 247 | 248 | method_exchangeImplementations(class_getInstanceMethod(destination, selector), class_getInstanceMethod(destination, destSel)); 249 | } else { 250 | // If the method we are hooking is not implemented on the subclass at hook-time, 251 | // we want orig calls from those hooks to go to the superclass. (ZKOriginalImplementation 252 | // redirects to ZKSuperImplementation when destinationSelectorForSelector() is not implemented 253 | // on the target class, that, combined with the fact that ZKOrig is called from a hook, means 254 | // calls should redirect to super) 255 | success &= class_addMethod(destination, selector, method_getImplementation(method), method_getTypeEncoding(method)); 256 | } 257 | 258 | } else { 259 | // Add any extra methods to the class but don't swizzle them 260 | success &= class_addMethod(destination, selector, method_getImplementation(method), method_getTypeEncoding(method)); 261 | } 262 | } 263 | 264 | unsigned int propertyCount; 265 | objc_property_t *propertyList = class_copyPropertyList(source, &propertyCount); 266 | for (int i = 0; i < propertyCount; i++) { 267 | objc_property_t property = propertyList[i]; 268 | const char *name = property_getName(property); 269 | unsigned int attributeCount; 270 | objc_property_attribute_t *attributes = property_copyAttributeList(property, &attributeCount); 271 | 272 | if (class_getProperty(destination, name) == NULL) { 273 | class_addProperty(destination, name, attributes, attributeCount); 274 | } else { 275 | class_replaceProperty(destination, name, attributes, attributeCount); 276 | } 277 | 278 | free(attributes); 279 | } 280 | 281 | free(propertyList); 282 | free(methodList); 283 | return success; 284 | #endif 285 | } 286 | 287 | // Options were to use a group class and traverse its subclasses 288 | // or to create a groups dictionary 289 | // This works because +load on NSObject is called before attribute((constructor)) 290 | static NSMutableDictionary *groups = nil; 291 | void _$ZKRegisterInterface(Class cls, const char *groupName) { 292 | if (!groups) 293 | groups = [[NSMutableDictionary dictionary] retain]; 294 | 295 | NSString *groupString = @(groupName); 296 | NSMutableArray *groupList = groups[groupString]; 297 | if (!groupList) { 298 | groupList = [NSMutableArray array]; 299 | groups[groupString] = groupList; 300 | } 301 | 302 | [groupList addObject:NSStringFromClass(cls)]; 303 | } 304 | 305 | BOOL _ZKSwizzleGroup(const char *groupName) { 306 | NSArray *groupList = groups[@(groupName)]; 307 | if (!groupList) { 308 | [NSException raise:@"Invalid Argument" format:@"ZKSwizzle: There is no group by the name of %s", groupName]; 309 | return NO; 310 | } 311 | 312 | BOOL success = YES; 313 | for (NSString *className in groupList) { 314 | Class cls = NSClassFromString(className); 315 | if (cls == NULL) 316 | continue; 317 | 318 | if (class_respondsToSelector(object_getClass(cls), @selector(_ZK_unconditionallySwizzle))) { 319 | [cls _ZK_unconditionallySwizzle]; 320 | } else { 321 | success = NO; 322 | } 323 | } 324 | 325 | return success; 326 | } 327 | -------------------------------------------------------------------------------- /ZKSwizzleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ZKSwizzleTests/ZKTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZKTests.m 3 | // ZKSwizzle 4 | // 5 | // Created by Alexander S Zielenski on 7/24/14. 6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "ZKSwizzle.h" 12 | 13 | id bloxecute(id (^block)()) { 14 | return block(); 15 | } 16 | 17 | @interface ZKOriginalClass : NSObject { 18 | int ivar; 19 | } 20 | + (NSString *)classMethod; 21 | + (NSString *)description; 22 | - (NSString *)instanceMethod; 23 | - (NSString *)description; 24 | - (int)ivar; 25 | @end 26 | 27 | @interface ZKOriginalClass (meh) 28 | - (NSString *)addedMethod; 29 | @end 30 | 31 | @implementation ZKOriginalClass 32 | - (id)init { if ((self = [super init])) { ivar = 1; } return self; } 33 | + (BOOL)isSubclassOfClass:(Class)aClass { return YES; } 34 | + (NSString *)classMethod { return @"original"; } 35 | + (NSString *)description { return @"original"; } 36 | - (NSString *)instanceMethod { return @"original"; } 37 | - (NSString *)description { return @"original"; } 38 | - (int)ivar { return ivar; } 39 | - (NSString *)selectorName { return NSStringFromSelector(_cmd); } 40 | @end 41 | 42 | @interface ZKSwizzlerClass : ZKOriginalClass @end 43 | @implementation ZKSwizzlerClass 44 | 45 | + (BOOL)isSubclassOfClass:(Class)aClass { 46 | return ZKSuper(BOOL, aClass); 47 | } 48 | 49 | - (NSString *)className { 50 | return [ZKOrig(NSString *) stringByAppendingString:@"_replaced"]; 51 | } 52 | 53 | + (NSString *)classMethod { 54 | return @"replaced"; 55 | } 56 | 57 | + (NSString *)description { 58 | return ZKSuper(NSString *); 59 | } 60 | 61 | - (NSString *)instanceMethod { 62 | return @"replaced"; 63 | } 64 | 65 | - (NSString *)description { 66 | return ZKSuper(NSString *); 67 | } 68 | 69 | - (int)ivar { 70 | int *hooked = &ZKHookIvar(self, int, "ivar"); 71 | *hooked = 3; 72 | return _orig(int); 73 | } 74 | 75 | - (NSString *)selectorName { 76 | return bloxecute(^{ 77 | return _orig(NSString *); 78 | }); 79 | } 80 | 81 | - (NSString *)addedMethod { 82 | // NSLog(@"%@", ZKOrig(NSString *)); 83 | return @"hi"; 84 | } 85 | 86 | @end 87 | 88 | @interface ZKSwizzlerClass2 : ZKOriginalClass 89 | 90 | @end 91 | @implementation ZKSwizzlerClass2 92 | 93 | - (NSString *)selectorName { 94 | return [NSString stringWithFormat:@"BREH: %@", ZKOrig(NSString *)]; 95 | } 96 | 97 | - (NSString *)description { 98 | return [@"MULTIPLE: " stringByAppendingString: ZKOrig(NSString *)]; 99 | } 100 | 101 | @end 102 | 103 | @interface DummyClass : NSObject 104 | @end 105 | 106 | @implementation DummyClass 107 | 108 | - (NSString *)description { 109 | return @"DummyClass"; 110 | } 111 | 112 | @end 113 | 114 | @interface NewClass : DummyClass 115 | @end 116 | 117 | @implementation NewClass 118 | 119 | - (NSString *)description { 120 | return ZKSuper(NSString *); 121 | } 122 | 123 | @end 124 | 125 | @interface OtherClass : DummyClass 126 | @end 127 | 128 | @implementation OtherClass 129 | 130 | 131 | @end 132 | 133 | hook(OtherClass) 134 | 135 | - (NSString *)description { 136 | return [ZKOrig(NSString *) stringByAppendingString:@" 2"]; 137 | } 138 | 139 | endhook 140 | 141 | hook(DummyClass) 142 | 143 | - (NSString *)description { 144 | return @"DummyClass_hooked"; 145 | } 146 | 147 | endhook 148 | 149 | @interface GroupClass : NSObject 150 | + (NSString *)classMethod; 151 | - (NSString *)instanceMethod; 152 | @end 153 | 154 | @implementation GroupClass 155 | 156 | + (NSString *)classMethod { 157 | return @"classMethod"; 158 | } 159 | 160 | - (NSString *)instanceMethod { 161 | return @"instanceMethod"; 162 | } 163 | 164 | @end 165 | 166 | // Swizzled Group 167 | hook(GroupClass, Yosemite) 168 | 169 | + (BOOL)_ZK_ignoreTypes { 170 | return YES; 171 | } 172 | 173 | + (NSString *)classMethod { 174 | return @"swizzled"; 175 | } 176 | 177 | - (NSString *)instanceMethod { 178 | return @"swizzled"; 179 | } 180 | 181 | endhook 182 | 183 | // Unswizzled Group – unused 184 | hook(GroupClass, Mavericks) 185 | 186 | + (NSString *)classMethod { 187 | return @"swizzled2"; 188 | } 189 | 190 | - (NSString *)instanceMethod { 191 | return @"swizzled2"; 192 | } 193 | 194 | endhook 195 | 196 | ctor { 197 | ZKSwizzleGroup(Yosemite); 198 | } 199 | 200 | @interface ZKTests : XCTestCase 201 | @end 202 | 203 | @implementation ZKTests 204 | 205 | - (void)setUp { 206 | [super setUp]; 207 | } 208 | 209 | - (void)testExample { 210 | _ZKSwizzleClass(ZKClass(ZKSwizzlerClass)); 211 | _ZKSwizzleClass(ZKClass(ZKSwizzlerClass2)); 212 | ZKOriginalClass *instance = [[ ZKOriginalClass alloc] init]; 213 | XCTAssertEqualObjects([ ZKOriginalClass classMethod], @"replaced", @"replacing class methods"); 214 | XCTAssertEqualObjects([instance instanceMethod], @"replaced", @"replacing instance methods"); 215 | XCTAssertNotEqualObjects([ ZKOriginalClass description], @"original", @"calling super on class"); 216 | XCTAssertNotEqualObjects([instance description], @"original", @"calling super on instance"); 217 | XCTAssertEqual([ ZKOriginalClass isSubclassOfClass:[NSString class]], NO, @"calling super imp on class"); 218 | XCTAssertEqualObjects([instance className], @"ZKOriginalClass_replaced", @"calling original imp on instance"); 219 | XCTAssertEqualObjects([instance selectorName], @"BREH: selectorName", @"_cmd correct on original imps"); 220 | XCTAssertEqual([instance ivar], 3, @"hooking ivars"); 221 | XCTAssertEqual([instance addedMethod], @"hi", @"adding methods"); 222 | XCTAssertEqualObjects([[[NewClass alloc] init] description], @"DummyClass_hooked", @"ZKSuper outside of swizzling"); 223 | XCTAssertEqualObjects([[[OtherClass alloc] init] description], @"DummyClass_hooked 2"); 224 | } 225 | 226 | - (void)testGroups { 227 | XCTAssertEqualObjects([[[GroupClass alloc] init] instanceMethod], @"swizzled"); 228 | XCTAssertEqualObjects([GroupClass classMethod], @"swizzled"); 229 | } 230 | 231 | @end 232 | -------------------------------------------------------------------------------- /iOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /iOSTests/iOSTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // ZKTests.m 3 | // ZKSwizzle 4 | // 5 | // Created by Alexander S Zielenski on 7/24/14. 6 | // Copyright (c) 2014 Alexander S Zielenski. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "ZKSwizzle.h" 12 | 13 | id bloxecute(id (^block)()) { 14 | return block(); 15 | } 16 | 17 | @interface ZKOriginalClass : NSObject { 18 | int ivar; 19 | } 20 | + (NSString *)classMethod; 21 | + (NSString *)description; 22 | - (NSString *)instanceMethod; 23 | - (NSString *)description; 24 | - (int)ivar; 25 | @end 26 | 27 | @interface ZKOriginalClass (meh) 28 | - (NSString *)addedMethod; 29 | @end 30 | 31 | @implementation ZKOriginalClass 32 | - (id)init { if ((self = [super init])) { ivar = 1; } return self; } 33 | + (BOOL)isSubclassOfClass:(Class)aClass { return YES; } 34 | + (NSString *)classMethod { return @"original"; } 35 | + (NSString *)description { return @"original"; } 36 | - (NSString *)instanceMethod { return @"original"; } 37 | - (NSString *)description { return @"original"; } 38 | - (int)ivar { return ivar; } 39 | - (NSString *)selectorName { return NSStringFromSelector(_cmd); } 40 | @end 41 | 42 | @interface ZKSwizzlerClass : ZKOriginalClass @end 43 | @implementation ZKSwizzlerClass 44 | 45 | + (BOOL)isSubclassOfClass:(Class)aClass { 46 | return ZKSuper(BOOL, aClass); 47 | } 48 | 49 | - (NSString *)className { 50 | return [ZKOrig(NSString *) stringByAppendingString:@"_replaced"]; 51 | } 52 | 53 | + (NSString *)classMethod { 54 | return @"replaced"; 55 | } 56 | 57 | + (NSString *)description { 58 | return ZKSuper(NSString *); 59 | } 60 | 61 | - (NSString *)instanceMethod { 62 | return @"replaced"; 63 | } 64 | 65 | - (NSString *)description { 66 | return ZKSuper(NSString *); 67 | } 68 | 69 | - (int)ivar { 70 | int *hooked = &ZKHookIvar(self, int, "ivar"); 71 | *hooked = 3; 72 | return ZKOrig(int); 73 | } 74 | 75 | - (NSString *)selectorName { 76 | return bloxecute(^{ 77 | return ZKOrig(NSString *); 78 | }); 79 | } 80 | 81 | - (NSString *)addedMethod { 82 | // NSLog(@"%@", ZKOrig(NSString *)); 83 | return @"hi"; 84 | } 85 | 86 | @end 87 | 88 | @interface ZKSwizzlerClass2 : ZKOriginalClass 89 | 90 | @end 91 | @implementation ZKSwizzlerClass2 92 | 93 | - (NSString *)selectorName { 94 | return [NSString stringWithFormat:@"BREH: %@", ZKOrig(NSString *)]; 95 | } 96 | 97 | - (NSString *)description { 98 | return [@"MULTIPLE: " stringByAppendingString: ZKOrig(NSString *)]; 99 | } 100 | 101 | @end 102 | 103 | @interface DummyClass : NSObject 104 | @end 105 | 106 | @implementation DummyClass 107 | 108 | - (NSString *)description { 109 | return @"DummyClass"; 110 | } 111 | 112 | @end 113 | 114 | @interface NewClass : DummyClass 115 | @end 116 | 117 | @implementation NewClass 118 | 119 | - (NSString *)description { 120 | return ZKSuper(NSString *); 121 | } 122 | 123 | @end 124 | 125 | @interface GroupClass : NSObject 126 | + (NSString *)classMethod; 127 | - (NSString *)instanceMethod; 128 | @end 129 | 130 | @implementation GroupClass 131 | 132 | + (NSString *)classMethod { 133 | return @"classMethod"; 134 | } 135 | 136 | - (NSString *)instanceMethod { 137 | return @"instanceMethod"; 138 | } 139 | 140 | @end 141 | 142 | // Swizzled Group 143 | ZKSwizzleInterfaceGroup(GroupSwizzle, GroupClass, NSObject, Yosemite) 144 | @implementation GroupSwizzle 145 | 146 | + (NSString *)classMethod { 147 | return @"swizzled"; 148 | } 149 | 150 | - (NSString *)instanceMethod { 151 | return @"swizzled"; 152 | } 153 | 154 | @end 155 | 156 | // Unswizzled Group – unused 157 | ZKSwizzleInterfaceGroup(GroupSwizzle2, GroupClass, NSObject, Mavericks) 158 | @implementation GroupSwizzle2 159 | 160 | + (NSString *)classMethod { 161 | return @"swizzled2"; 162 | } 163 | 164 | - (NSString *)instanceMethod { 165 | return @"swizzled2"; 166 | } 167 | 168 | @end 169 | #define ctor __attribute__((constructor)) void init() 170 | 171 | ctor { 172 | ZKSwizzleGroup(Yosemite); 173 | } 174 | 175 | @interface ZKTests : XCTestCase 176 | @end 177 | 178 | @implementation ZKTests 179 | 180 | - (void)setUp { 181 | [super setUp]; 182 | } 183 | 184 | - (void)testExample { 185 | _ZKSwizzleClass(ZKClass(ZKSwizzlerClass)); 186 | _ZKSwizzleClass(ZKClass(ZKSwizzlerClass2)); 187 | ZKOriginalClass *instance = [[ ZKOriginalClass alloc] init]; 188 | XCTAssertEqualObjects([ ZKOriginalClass classMethod], @"replaced", @"replacing class methods"); 189 | XCTAssertEqualObjects([instance instanceMethod], @"replaced", @"replacing instance methods"); 190 | XCTAssertNotEqualObjects([ ZKOriginalClass description], @"original", @"calling super on class"); 191 | XCTAssertNotEqualObjects([instance description], @"original", @"calling super on instance"); 192 | XCTAssertEqual([ ZKOriginalClass isSubclassOfClass:[NSString class]], NO, @"calling super imp on class"); 193 | // XCTAssertEqualObjects([instance className], @"ZKOriginalClass_replaced", @"calling original imp on instance"); 194 | XCTAssertEqualObjects([instance selectorName], @"BREH: selectorName", @"_cmd correct on original imps"); 195 | XCTAssertEqual([instance ivar], 3, @"hooking ivars"); 196 | XCTAssertEqual([instance addedMethod], @"hi", @"adding methods"); 197 | XCTAssertEqualObjects([[[NewClass alloc] init] description], @"DummyClass", @"ZKSuper outside of swizzling"); 198 | } 199 | 200 | - (void)testGroups { 201 | // ZKSwizzleGroup(Yosemite); 202 | XCTAssertEqualObjects([[[GroupClass alloc] init] instanceMethod], @"swizzled"); 203 | XCTAssertEqualObjects([GroupClass classMethod], @"swizzled"); 204 | } 205 | 206 | @end 207 | --------------------------------------------------------------------------------