├── .gitmodules ├── CTObjectiveCRuntimeAdditions ├── CTObjectiveCRuntimeAdditionsTests │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── CTObjectiveCRuntimeAdditionsTests.h │ ├── CTTestSwizzleSubclass.h │ ├── CTObjectiveCRuntimeAdditionsTests-Info.plist │ ├── CTTestSwizzleSubclass.m │ ├── CTTestSwizzleClass.h │ ├── CTTestSwizzleClass.m │ └── CTObjectiveCRuntimeAdditionsTests.m ├── CTObjectiveCRuntimeAdditions │ ├── CTObjectiveCRuntimeAdditions-Prefix.pch │ ├── CTBlockDescription.h │ ├── CTObjectiveCRuntimeAdditions.h │ ├── CTBlockDescription.m │ └── CTObjectiveCRuntimeAdditions.m └── CTObjectiveCRuntimeAdditions.xcodeproj │ └── project.pbxproj ├── .gitignore ├── LICENSE └── README.md /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | build/ 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | *.xcworkspace 12 | !default.xcworkspace 13 | xcuserdata 14 | profile 15 | *.moved-aside 16 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CTObjectiveCRuntimeAdditions' target in the 'CTObjectiveCRuntimeAdditions' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTObjectiveCRuntimeAdditionsTests.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTObjectiveCRuntimeAdditionsTests.h 3 | // CTObjectiveCRuntimeAdditionsTests 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright (c) 2012 Home. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CTObjectiveCRuntimeAdditionsTests : SenTestCase 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTTestSwizzleSubclass.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTTestSwizzleSubclass.h 3 | // CTObjectiveCRuntimeAdditions 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright 2012 ebf. All rights reserved. 7 | // 8 | 9 | #import "CTTestSwizzleClass.h" 10 | 11 | 12 | /** 13 | @abstract <#abstract comment#> 14 | */ 15 | @interface CTTestSwizzleSubclass : CTTestSwizzleClass { 16 | @private 17 | 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTObjectiveCRuntimeAdditionsTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | de.ebf.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTTestSwizzleSubclass.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTTestSwizzleSubclass.m 3 | // CTObjectiveCRuntimeAdditions 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright 2012 ebf. All rights reserved. 7 | // 8 | 9 | #import "CTTestSwizzleSubclass.h" 10 | 11 | 12 | 13 | @interface CTTestSwizzleSubclass () { 14 | 15 | } 16 | 17 | @end 18 | 19 | 20 | 21 | @implementation CTTestSwizzleSubclass 22 | 23 | #pragma mark - Initialization 24 | 25 | - (id)init 26 | { 27 | if (self = [super init]) { 28 | // Initialization code 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark - Instance methods 34 | 35 | + (BOOL)passesTest 36 | { 37 | return YES; 38 | } 39 | 40 | #pragma mark - Memory management 41 | 42 | - (void)dealloc 43 | { 44 | 45 | } 46 | 47 | #pragma mark - Private category implementation () 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 EBF-EDV Beratung Föllmer GmbH 2 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 3 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 4 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTTestSwizzleClass.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTTestSwizzleClass.h 3 | // CTObjectiveCRuntimeAdditions 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright 2012 ebf. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | @abstract <#abstract comment#> 13 | */ 14 | @interface CTTestSwizzleClass : NSObject { 15 | @private 16 | 17 | } 18 | 19 | - (NSString *)orginalString; 20 | - (NSString *)__hookedOriginalString; 21 | 22 | - (NSString *)stringTwo; 23 | - (NSString *)__prefixedHookedStringTwo; 24 | 25 | + (NSString *)stringThree; 26 | + (NSString *)__prefixedHookedStringThree; 27 | 28 | - (NSString *)stringByJoiningString:(NSString *)string withWith:(NSString *)suffix; 29 | - (NSString *)__prefixedHookedStringByJoiningString:(NSString *)string withWith:(NSString *)suffix; 30 | 31 | - (NSString *)__prefixedHookedStringFive; 32 | 33 | + (BOOL)passesTest; 34 | 35 | - (NSString *)helloWorldStringFromString:(NSString *)string; 36 | 37 | - (CGPoint)pointByAddingPoint:(CGPoint)point; 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions/CTBlockDescription.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTBlockDescription.h 3 | // CTBlockDescription 4 | // 5 | // Created by Oliver Letterer on 01.09.12. 6 | // Copyright (c) 2012 olettere. All rights reserved. 7 | // 8 | 9 | struct CTBlockLiteral { 10 | void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock 11 | int flags; 12 | int reserved; 13 | void (*invoke)(void *, ...); 14 | struct block_descriptor { 15 | unsigned long int reserved; // NULL 16 | unsigned long int size; // sizeof(struct Block_literal_1) 17 | // optional helper functions 18 | void (*copy_helper)(void *dst, void *src); // IFF (1<<25) 19 | void (*dispose_helper)(void *src); // IFF (1<<25) 20 | // required ABI.2010.3.16 21 | const char *signature; // IFF (1<<30) 22 | } *descriptor; 23 | // imported variables 24 | }; 25 | 26 | enum { 27 | CTBlockDescriptionFlagsHasCopyDispose = (1 << 25), 28 | CTBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code 29 | CTBlockDescriptionFlagsIsGlobal = (1 << 28), 30 | CTBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE 31 | CTBlockDescriptionFlagsHasSignature = (1 << 30) 32 | }; 33 | typedef int CTBlockDescriptionFlags; 34 | 35 | 36 | 37 | @interface CTBlockDescription : NSObject 38 | 39 | @property (nonatomic, readonly) CTBlockDescriptionFlags flags; 40 | @property (nonatomic, readonly) NSMethodSignature *blockSignature; 41 | @property (nonatomic, readonly) unsigned long int size; 42 | @property (nonatomic, readonly) id block; 43 | 44 | - (id)initWithBlock:(id)block; 45 | 46 | - (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature; 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // CTObjectiveCRuntimeAdditions.h 3 | // CTObjectiveCRuntimeAdditions 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright (c) 2012 ebf. All rights reserved. 7 | // 8 | 9 | typedef void(^CTMethodEnumertor)(Class class, Method method); 10 | typedef BOOL(^CTClassTest)(Class subclass); 11 | 12 | /** 13 | @abstract Swizzles originalSelector with newSelector. 14 | */ 15 | void class_swizzleSelector(Class class, SEL originalSelector, SEL newSelector); 16 | 17 | /** 18 | @abstract Swizzles all methods of a class with a given prefix with the corresponding SEL without the prefix. @selector(__hookedLoadView) will be swizzled with @selector(loadView). This method also swizzles class methods with a given prefix. 19 | */ 20 | void class_swizzlesMethodsWithPrefix(Class class, NSString *prefix); 21 | 22 | /** 23 | @abstract Enumerate class methods. 24 | */ 25 | void class_enumerateMethodList(Class class, CTMethodEnumertor enumerator); 26 | 27 | /** 28 | @return A subclass of class which passes test. 29 | */ 30 | Class class_subclassPassingTest(Class class, CTClassTest test); 31 | 32 | /** 33 | @abstract Replaces implementation of method of originalSelector with block. 34 | if originalSelector's argument list is (id self, SEL _cmd, ...), then block's argument list must be (id self, ...) 35 | */ 36 | IMP class_replaceMethodWithBlock(Class class, SEL originalSelector, id block); 37 | 38 | /** 39 | Implements class property at runtime which is backed by NSUserDefaults. This will use -[NSUserDefaults setObject:forKey:]. 40 | */ 41 | void class_implementPropertyInUserDefaults(Class class, NSString *propertyName, BOOL automaticSynchronizeUserDefaults); 42 | 43 | /** 44 | Implements a property at runtime. 45 | */ 46 | void class_implementProperty(Class class, NSString *propertyName); 47 | 48 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTTestSwizzleClass.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTTestSwizzleClass.m 3 | // CTObjectiveCRuntimeAdditions 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright 2012 ebf. All rights reserved. 7 | // 8 | 9 | #import "CTTestSwizzleClass.h" 10 | 11 | 12 | 13 | @interface CTTestSwizzleClass () { 14 | 15 | } 16 | 17 | @end 18 | 19 | 20 | 21 | @implementation CTTestSwizzleClass 22 | 23 | #pragma mark - Initialization 24 | 25 | - (id)init 26 | { 27 | if (self = [super init]) { 28 | // Initialization code 29 | } 30 | return self; 31 | } 32 | 33 | #pragma mark - Instance methods 34 | 35 | - (CGPoint)pointByAddingPoint:(CGPoint)point 36 | { 37 | return CGPointMake(point.x + 1.0f, point.y + 1.0f); 38 | } 39 | 40 | - (NSString *)helloWorldStringFromString:(NSString *)string 41 | { 42 | return [@"Hello World" stringByAppendingFormat:@" %@", string]; 43 | } 44 | 45 | - (NSString *)orginalString 46 | { 47 | return @"foo"; 48 | } 49 | 50 | - (NSString *)__hookedOriginalString 51 | { 52 | NSString *original = [self __hookedOriginalString]; 53 | return [original stringByAppendingString:@"bar"]; 54 | } 55 | 56 | - (NSString *)stringTwo 57 | { 58 | return @"foo"; 59 | } 60 | 61 | - (NSString *)__prefixedHookedStringTwo 62 | { 63 | NSString *original = [self __prefixedHookedStringTwo]; 64 | return [original stringByAppendingString:@"bar"]; 65 | } 66 | 67 | + (NSString *)stringThree 68 | { 69 | return @"foo"; 70 | } 71 | 72 | + (NSString *)__prefixedHookedStringThree 73 | { 74 | NSString *original = [self __prefixedHookedStringThree]; 75 | return [original stringByAppendingString:@"bar"]; 76 | } 77 | 78 | - (NSString *)stringByJoiningString:(NSString *)string withWith:(NSString *)suffix 79 | { 80 | return [string stringByAppendingString:suffix]; 81 | } 82 | 83 | - (NSString *)__prefixedHookedStringByJoiningString:(NSString *)string withWith:(NSString *)suffix 84 | { 85 | NSString *original = [self __prefixedHookedStringByJoiningString:string withWith:suffix]; 86 | return [original stringByAppendingString:@"bar"]; 87 | } 88 | 89 | - (NSString *)__prefixedHookedStringFive 90 | { 91 | return @"foo"; 92 | } 93 | 94 | + (BOOL)passesTest 95 | { 96 | return NO; 97 | } 98 | 99 | #pragma mark - Memory management 100 | 101 | - (void)dealloc 102 | { 103 | 104 | } 105 | 106 | #pragma mark - Private category implementation () 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions/CTBlockDescription.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTBlockDescription.m 3 | // CTBlockDescription 4 | // 5 | // Created by Oliver Letterer on 01.09.12. 6 | // Copyright (c) 2012 olettere. All rights reserved. 7 | // 8 | 9 | #import "CTBlockDescription.h" 10 | 11 | @implementation CTBlockDescription 12 | 13 | - (id)initWithBlock:(id)block 14 | { 15 | if (self = [super init]) { 16 | _block = block; 17 | 18 | struct CTBlockLiteral *blockRef = (__bridge struct CTBlockLiteral *)block; 19 | _flags = blockRef->flags; 20 | _size = blockRef->descriptor->size; 21 | 22 | if (_flags & CTBlockDescriptionFlagsHasSignature) { 23 | void *signatureLocation = blockRef->descriptor; 24 | signatureLocation += sizeof(unsigned long int); 25 | signatureLocation += sizeof(unsigned long int); 26 | 27 | if (_flags & CTBlockDescriptionFlagsHasCopyDispose) { 28 | signatureLocation += sizeof(void(*)(void *dst, void *src)); 29 | signatureLocation += sizeof(void (*)(void *src)); 30 | } 31 | 32 | const char *signature = (*(const char **)signatureLocation); 33 | _blockSignature = [NSMethodSignature signatureWithObjCTypes:signature]; 34 | } 35 | } 36 | return self; 37 | } 38 | 39 | - (BOOL)isCompatibleForBlockSwizzlingWithMethodSignature:(NSMethodSignature *)methodSignature 40 | { 41 | if (_blockSignature.numberOfArguments != methodSignature.numberOfArguments + 1) { 42 | return NO; 43 | } 44 | 45 | if (strcmp(_blockSignature.methodReturnType, methodSignature.methodReturnType) != 0) { 46 | return NO; 47 | } 48 | 49 | for (int i = 0; i < methodSignature.numberOfArguments; i++) { 50 | if (i == 1) { 51 | // SEL in method, IMP in block 52 | if (strcmp([methodSignature getArgumentTypeAtIndex:i], ":") != 0) { 53 | return NO; 54 | } 55 | 56 | if (strcmp([_blockSignature getArgumentTypeAtIndex:i + 1], "^?") != 0) { 57 | return NO; 58 | } 59 | } else { 60 | if (strcmp([methodSignature getArgumentTypeAtIndex:i], [_blockSignature getArgumentTypeAtIndex:i + 1]) != 0) { 61 | return NO; 62 | } 63 | } 64 | } 65 | 66 | return YES; 67 | } 68 | 69 | - (NSString *)description 70 | { 71 | return [NSString stringWithFormat:@"%@: %@", [super description], _blockSignature.description]; 72 | } 73 | 74 | @end 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CTObjectiveCRuntimeAdditions 2 | ============================ 3 | 4 | CTObjectiveCRuntimeAdditions introduces the following new runtime additions: 5 | 6 | * `void class_swizzleSelector(Class class, SEL originalSelector, SEL newSelector);` 7 | * `void class_swizzlesMethodsWithPrefix(Class class, NSString *prefix);` 8 | * `void class_enumerateMethodList(Class class, CTMethodEnumertor enumerator);` 9 | * `Class class_subclassPassingTest(Class class, CTClassTest test);` 10 | * `IMP class_replaceMethodWithBlock(Class class, SEL originalSelector, id block);` 11 | * `void class_implementPropertyInUserDefaults(Class class, NSString *propertyName, BOOL automaticSynchronizeUserDefaults);` 12 | * `void class_implementProperty(Class class, NSString *propertyName, objc_AssociationPolicy associationPolicy);` 13 | 14 | 15 | Getting runtime information about blocks 16 | ============================ 17 | [CTBlockDescription](https://github.com/ebf/CTObjectiveCRuntimeAdditions/blob/master/CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions/CTBlockDescription.h) lets you inspect blocks including arguments and compile time features at runtime. 18 | 19 | One could use CTBlockDescription for the following test block: 20 | 21 | ``` objc 22 | // a test block. 23 | BOOL(^testBlock)(BOOL animated, id object) = ^BOOL(BOOL animated, id object) { 24 | return YES; 25 | }; 26 | 27 | // allocating a block description 28 | CTBlockDescription *blockDescription = [[CTBlockDescription alloc] initWithBlock:testBlock]; 29 | 30 | // getting a method signature for this block 31 | NSMethodSignature *methodSignature = blockDescription.blockSignature; 32 | /** 33 | 34 | number of arguments = 3 35 | frame size = 12 36 | is special struct return? NO 37 | return value: -------- -------- -------- -------- 38 | type encoding (c) 'c' 39 | flags {isSigned} 40 | modifiers {} 41 | frame {offset = 0, offset adjust = 0, size = 4, size adjust = -3} 42 | memory {offset = 0, size = 1} 43 | argument 0: -------- -------- -------- -------- 44 | type encoding (@) '@?' 45 | flags {isObject, isBlock} 46 | modifiers {} 47 | frame {offset = 0, offset adjust = 0, size = 4, size adjust = 0} 48 | memory {offset = 0, size = 4} 49 | argument 1: -------- -------- -------- -------- 50 | type encoding (c) 'c' 51 | flags {isSigned} 52 | modifiers {} 53 | frame {offset = 4, offset adjust = 0, size = 4, size adjust = -3} 54 | memory {offset = 0, size = 1} 55 | argument 2: -------- -------- -------- -------- 56 | type encoding (@) '@' 57 | flags {isObject} 58 | modifiers {} 59 | frame {offset = 8, offset adjust = 0, size = 4, size adjust = 0} 60 | memory {offset = 0, size = 4} 61 | */ 62 | ``` 63 | 64 | License 65 | ============================ 66 | MIT 67 | 68 | Thanks goes to the [Block Implementation Specification](http://clang.llvm.org/docs/Block-ABI-Apple.txt) and the [A2DynamicDelegate](https://github.com/pandamonia/A2DynamicDelegate) project for some good libffi samples and convertion from encodings to libffi types. 69 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditionsTests/CTObjectiveCRuntimeAdditionsTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTObjectiveCRuntimeAdditionsTests.m 3 | // CTObjectiveCRuntimeAdditionsTests 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright (c) 2012 Home. All rights reserved. 7 | // 8 | 9 | #import "CTObjectiveCRuntimeAdditionsTests.h" 10 | #import "CTObjectiveCRuntimeAdditions.h" 11 | #import "CTTestSwizzleClass.h" 12 | #import "CTTestSwizzleSubclass.h" 13 | #import "CTBlockDescription.h" 14 | 15 | @implementation CTObjectiveCRuntimeAdditionsTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | 21 | // Set-up code here. 22 | } 23 | 24 | - (void)tearDown 25 | { 26 | // Tear-down code here. 27 | 28 | [super tearDown]; 29 | } 30 | 31 | - (void)testBlockDescription 32 | { 33 | BOOL(^testBlock)(BOOL animated, id object) = ^BOOL(BOOL animated, id object) { 34 | return YES; 35 | }; 36 | 37 | CTBlockDescription *blockDescription = [[CTBlockDescription alloc] initWithBlock:testBlock]; 38 | NSMethodSignature *methodSignature = blockDescription.blockSignature; 39 | 40 | STAssertEquals(strcmp(methodSignature.methodReturnType, @encode(BOOL)), 0, @"return type wrong"); 41 | 42 | const char *expectedArguments[] = {@encode(typeof(testBlock)), @encode(BOOL), @encode(id)}; 43 | for (int i = 0; i < blockDescription.blockSignature.numberOfArguments; i++) { 44 | STAssertEquals(strcmp([blockDescription.blockSignature getArgumentTypeAtIndex:i], expectedArguments[i]), 0, @"Argument %d wrong", i); 45 | } 46 | } 47 | 48 | - (void)testBlockSwizzling 49 | { 50 | Class class = [CTTestSwizzleClass class]; 51 | CTTestSwizzleClass *testObject = [[CTTestSwizzleClass alloc] init]; 52 | NSString *originalString = [testObject helloWorldStringFromString:@"Oli"]; 53 | 54 | STAssertEqualObjects(originalString, @"Hello World Oli", @"Original helloWorldString wrong"); 55 | 56 | __block IMP implementation1 = class_replaceMethodWithBlock(class, @selector(helloWorldStringFromString:), ^NSString *(CTTestSwizzleClass *blockSelf, NSString *string) { 57 | STAssertEqualObjects(blockSelf, testObject, @"blockSelf is wrong"); 58 | return [implementation1(blockSelf, @selector(helloWorldStringFromString:), string) stringByAppendingFormat:@" Hooked"]; 59 | }); 60 | 61 | STAssertEqualObjects([testObject helloWorldStringFromString:@"Oli"], @"Hello World Oli Hooked", @"did not swizzle with block"); 62 | 63 | __block IMP implementation2 = class_replaceMethodWithBlock(class, @selector(helloWorldStringFromString:), ^NSString *(CTTestSwizzleClass *blockSelf, NSString *string) { 64 | STAssertEqualObjects(blockSelf, testObject, @"blockSelf is wrong"); 65 | return [implementation2(blockSelf, @selector(helloWorldStringFromString:), string) stringByAppendingFormat:@" Hooked2"]; 66 | }); 67 | 68 | STAssertEqualObjects([testObject helloWorldStringFromString:@"Oli"], @"Hello World Oli Hooked Hooked2", @"did not swizzle with block"); 69 | 70 | 71 | // test structs 72 | STAssertEquals(CGPointMake(2.0f, 2.0f), [testObject pointByAddingPoint:CGPointMake(1.0f, 1.0f)], @"initial point wrong"); 73 | 74 | typedef CGPoint(*pointImplementation)(id self, SEL _cmd, CGPoint point); 75 | 76 | __block pointImplementation implementation3 = (pointImplementation)class_replaceMethodWithBlock(class, @selector(pointByAddingPoint:), ^CGPoint(CTTestSwizzleClass *blockSelf, CGPoint point) { 77 | STAssertEqualObjects(blockSelf, testObject, @"blockSelf is wrong"); 78 | 79 | CGPoint originalPoint = implementation3(blockSelf, @selector(pointByAddingPoint:), point); 80 | return CGPointMake(originalPoint.x + 1.0f, originalPoint.y + 1.0f); 81 | }); 82 | 83 | STAssertEquals(CGPointMake(3.0f, 3.0f), [testObject pointByAddingPoint:CGPointMake(1.0f, 1.0f)], @"initial point wrong"); 84 | } 85 | 86 | - (void)testMethodSwizzling 87 | { 88 | CTTestSwizzleClass *testObject = [[CTTestSwizzleClass alloc] init]; 89 | NSString *originalString = testObject.orginalString; 90 | 91 | class_swizzleSelector(CTTestSwizzleClass.class, @selector(orginalString), @selector(__hookedOriginalString)); 92 | 93 | NSString *hookedString = testObject.orginalString; 94 | 95 | STAssertFalse([originalString isEqualToString:hookedString], @"originalDescription cannot be equal to hookedDescription."); 96 | STAssertEqualObjects(originalString, @"foo", @"originalString wrong."); 97 | STAssertTrue([hookedString hasSuffix:@"foobar"], @"hookedDescription should have suffix 'bar'."); 98 | } 99 | 100 | - (void)testAutomaticMethodSwizzlingWithMethodPrefix 101 | { 102 | CTTestSwizzleClass *testObject = [[CTTestSwizzleClass alloc] init]; 103 | 104 | NSString *originalString = testObject.stringTwo; 105 | NSString *originalStringThree = [CTTestSwizzleClass stringThree]; 106 | NSString *originalJoinedString = [testObject stringByJoiningString:@"my" withWith:@"string"]; 107 | 108 | NSString *originalStringFive = testObject.__prefixedHookedStringFive; 109 | 110 | class_swizzlesMethodsWithPrefix(CTTestSwizzleClass.class, @"__prefixedHooked"); 111 | 112 | NSString *hookedString = testObject.stringTwo; 113 | NSString *hookedStringThree = [CTTestSwizzleClass stringThree]; 114 | NSString *hookedJoinedString = [testObject stringByJoiningString:@"my" withWith:@"string"]; 115 | 116 | STAssertFalse([originalString isEqualToString:hookedString], @"originalString cannot be equal to hookedString."); 117 | STAssertEqualObjects(originalString, @"foo", @"originalString wrong."); 118 | STAssertTrue([hookedString hasSuffix:@"foobar"], @"hookedString should have suffix 'bar'."); 119 | 120 | STAssertFalse([originalStringThree isEqualToString:hookedStringThree], @"originalStringThree cannot be equal to hookedStringThree."); 121 | STAssertEqualObjects(originalStringThree, @"foo", @"originalStringThree wrong."); 122 | STAssertTrue([hookedStringThree hasSuffix:@"foobar"], @"hookedStringThree should have suffix 'bar'."); 123 | 124 | STAssertFalse([originalJoinedString isEqualToString:hookedJoinedString], @"originalJoinedString cannot be equal to hookedJoinedString."); 125 | STAssertEqualObjects(originalJoinedString, @"mystring", @"originalJoinedString wrong."); 126 | STAssertTrue([hookedJoinedString hasSuffix:@"mystringbar"], @"hookedJoinedString should have suffix 'bar'."); 127 | 128 | STAssertEqualObjects(originalStringFive, @"foo", @"something went wrong with swizzling a method that doesn't have an original method without the prefix."); 129 | } 130 | 131 | - (void)testDynamicSubclassFinding 132 | { 133 | Class subclass = class_subclassPassingTest(CTTestSwizzleClass.class, ^BOOL(__unsafe_unretained Class subclass) { 134 | return [subclass passesTest]; 135 | }); 136 | 137 | STAssertEqualObjects(subclass, CTTestSwizzleSubclass.class, @"subclass that passes test should be CTTestSwizzleSubclass"); 138 | } 139 | 140 | @end 141 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // CTObjectiveCRuntimeAdditions.m 3 | // CTObjectiveCRuntimeAdditions 4 | // 5 | // Created by Oliver Letterer on 28.04.12. 6 | // Copyright (c) 2012 ebf. All rights reserved. 7 | // 8 | 9 | #import "CTObjectiveCRuntimeAdditions.h" 10 | #import "CTBlockDescription.h" 11 | 12 | void class_swizzleSelector(Class class, SEL originalSelector, SEL newSelector) 13 | { 14 | Method origMethod = class_getInstanceMethod(class, originalSelector); 15 | Method newMethod = class_getInstanceMethod(class, newSelector); 16 | if(class_addMethod(class, originalSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { 17 | class_replaceMethod(class, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); 18 | } else { 19 | method_exchangeImplementations(origMethod, newMethod); 20 | } 21 | } 22 | 23 | void class_swizzlesMethodsWithPrefix(Class class, NSString *prefix) 24 | { 25 | CTMethodEnumertor enumeratorBlock = ^(Class class, Method method) { 26 | SEL methodSelector = method_getName(method); 27 | NSString *selectorString = NSStringFromSelector(methodSelector); 28 | 29 | if ([selectorString hasPrefix:prefix]) { 30 | NSMutableString *originalSelectorString = [selectorString stringByReplacingOccurrencesOfString:prefix withString:@"" options:NSLiteralSearch range:NSMakeRange(0, prefix.length)].mutableCopy; 31 | 32 | if (originalSelectorString.length > 0) { 33 | NSString *uppercaseFirstCharacter = [originalSelectorString substringToIndex:1]; 34 | NSString *lowercaseFirstCharacter = uppercaseFirstCharacter.lowercaseString; 35 | 36 | [originalSelectorString replaceCharactersInRange:NSMakeRange(0, 1) withString:lowercaseFirstCharacter]; 37 | 38 | SEL originalSelector = NSSelectorFromString(originalSelectorString); 39 | 40 | class_swizzleSelector(class, originalSelector, methodSelector); 41 | } 42 | } 43 | }; 44 | 45 | // swizzle instance methods 46 | class_enumerateMethodList(class, enumeratorBlock); 47 | 48 | // swizzle class methods 49 | Class metaClass = objc_getMetaClass(class_getName(class)); 50 | class_enumerateMethodList(metaClass, enumeratorBlock); 51 | } 52 | 53 | void class_enumerateMethodList(Class class, CTMethodEnumertor enumerator) 54 | { 55 | if (!enumerator) return; 56 | 57 | static dispatch_queue_t queue = NULL; 58 | 59 | static dispatch_once_t onceToken; 60 | dispatch_once(&onceToken, ^{ 61 | queue = dispatch_queue_create("de.ebf.objc_runtime_additions.method_enumeration_queue", DISPATCH_QUEUE_CONCURRENT); 62 | }); 63 | 64 | unsigned int methodCount = 0; 65 | Method *methods = class_copyMethodList(class, &methodCount); 66 | 67 | dispatch_apply(methodCount, queue, ^(size_t index) { 68 | Method method = methods[index]; 69 | enumerator(class, method); 70 | }); 71 | 72 | free(methods); 73 | } 74 | 75 | Class class_subclassPassingTest(Class class, CTClassTest test) 76 | { 77 | if (!test) return nil; 78 | 79 | static dispatch_queue_t queue = NULL; 80 | 81 | static dispatch_once_t onceToken; 82 | dispatch_once(&onceToken, ^{ 83 | queue = dispatch_queue_create("de.ebf.objc_runtime_additions.class_queue", DISPATCH_QUEUE_CONCURRENT); 84 | }); 85 | 86 | unsigned int numberOfClasses = 0; 87 | Class *classList = objc_copyClassList(&numberOfClasses); 88 | 89 | __block Class testPassingClass = nil; 90 | 91 | dispatch_apply(numberOfClasses, queue, ^(size_t classIndex) { 92 | if (testPassingClass != nil) { 93 | return; 94 | } 95 | 96 | Class thisClass = classList[classIndex]; 97 | Class superClass = thisClass; 98 | 99 | while ((superClass = class_getSuperclass(superClass))) { 100 | if (superClass == class || thisClass == class) { 101 | if (test(thisClass)) { 102 | testPassingClass = thisClass; 103 | } 104 | } 105 | } 106 | }); 107 | 108 | // cleanup 109 | free(classList); 110 | 111 | return testPassingClass; 112 | } 113 | 114 | IMP class_replaceMethodWithBlock(Class class, SEL originalSelector, id block) 115 | { 116 | IMP newImplementation = imp_implementationWithBlock(block); 117 | 118 | Method method = class_getInstanceMethod(class, originalSelector); 119 | return class_replaceMethod(class, originalSelector, newImplementation, method_getTypeEncoding(method)); 120 | } 121 | 122 | void class_implementPropertyInUserDefaults(Class class, NSString *propertyName, BOOL automaticSynchronizeUserDefaults) 123 | { 124 | NSString *userDefaultsKey = [NSString stringWithFormat:@"%@__%@", NSStringFromClass(class), propertyName]; 125 | 126 | SEL getter = NSSelectorFromString(propertyName); 127 | NSString *firstLetter = [propertyName substringToIndex:1]; 128 | NSString *setterName = [propertyName stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[NSString stringWithFormat:@"set%@", firstLetter.uppercaseString]]; 129 | setterName = [setterName stringByAppendingString:@":"]; 130 | SEL setter = NSSelectorFromString(setterName); 131 | 132 | IMP getterImplementation = imp_implementationWithBlock(^id(id self) { 133 | // 1) try to read from cache 134 | return [[NSUserDefaults standardUserDefaults] objectForKey:userDefaultsKey]; 135 | }); 136 | 137 | IMP setterImplementation = imp_implementationWithBlock(^(id self, id object) { 138 | if (!object) { 139 | [[NSUserDefaults standardUserDefaults] removeObjectForKey:userDefaultsKey]; 140 | } else { 141 | [[NSUserDefaults standardUserDefaults] setObject:object forKey:userDefaultsKey]; 142 | 143 | if (automaticSynchronizeUserDefaults) { 144 | [[NSUserDefaults standardUserDefaults] synchronize]; 145 | } 146 | } 147 | }); 148 | 149 | class_addMethod(class, getter, getterImplementation, "@@:"); 150 | class_addMethod(class, setter, setterImplementation, "v@:@"); 151 | } 152 | 153 | void class_implementProperty(Class class, NSString *propertyName) 154 | { 155 | NSCAssert(class != Nil, @"class is required"); 156 | NSCAssert(propertyName != nil, @"propertyName is required"); 157 | 158 | objc_property_t property = class_getProperty(class, propertyName.UTF8String); 159 | 160 | unsigned int count = 0; 161 | objc_property_attribute_t *attributes = property_copyAttributeList(property, &count); 162 | 163 | typedef enum { 164 | MemoryManagementAssign, 165 | MemoryManagementCopy, 166 | MemoryManagementRetain 167 | } MemoryManagement; 168 | 169 | MemoryManagement memoryManagement = MemoryManagementAssign; 170 | BOOL isNonatomic = NO; 171 | 172 | NSString *getterName = nil; 173 | NSString *setterName = nil; 174 | NSString *encoding = nil; 175 | 176 | for (int i = 0; i < count; i++) { 177 | objc_property_attribute_t attribute = attributes[i]; 178 | 179 | switch (attribute.name[0]) { 180 | case 'N': 181 | isNonatomic = YES; 182 | break; 183 | case '&': 184 | memoryManagement = MemoryManagementRetain; 185 | break; 186 | case 'C': 187 | memoryManagement = MemoryManagementCopy; 188 | break; 189 | case 'G': 190 | getterName = [NSString stringWithFormat:@"%s", attribute.value]; 191 | break; 192 | case 'S': 193 | setterName = [NSString stringWithFormat:@"%s", attribute.value]; 194 | break; 195 | case 'T': 196 | encoding = [NSString stringWithFormat:@"%s", attribute.value]; 197 | break; 198 | case 'W': 199 | NSCAssert(NO, @"weak properties are not supported"); 200 | break; 201 | default: 202 | break; 203 | } 204 | } 205 | 206 | if (!getterName) { 207 | getterName = propertyName; 208 | } 209 | 210 | if (!setterName) { 211 | NSString *firstLetter = [propertyName substringToIndex:1]; 212 | setterName = [propertyName stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[NSString stringWithFormat:@"set%@", firstLetter.uppercaseString]]; 213 | setterName = [setterName stringByAppendingString:@":"]; 214 | } 215 | 216 | NSCAssert([encoding characterAtIndex:0] != '{', @"structs are not supported"); 217 | NSCAssert([encoding characterAtIndex:0] != '(', @"unions are not supported"); 218 | 219 | SEL getter = NSSelectorFromString(getterName); 220 | SEL setter = NSSelectorFromString(setterName); 221 | 222 | if (encoding.UTF8String[0] == @encode(id)[0]) { 223 | IMP getterImplementation = imp_implementationWithBlock(^id(id self) { 224 | return objc_getAssociatedObject(self, getter); 225 | }); 226 | 227 | objc_AssociationPolicy associationPolicy = 0; 228 | 229 | if (memoryManagement == MemoryManagementCopy) { 230 | associationPolicy = isNonatomic ? OBJC_ASSOCIATION_COPY_NONATOMIC : OBJC_ASSOCIATION_COPY; 231 | } else { 232 | associationPolicy = isNonatomic ? OBJC_ASSOCIATION_RETAIN_NONATOMIC : OBJC_ASSOCIATION_RETAIN; 233 | } 234 | 235 | IMP setterImplementation = imp_implementationWithBlock(^(id self, id object) { 236 | objc_setAssociatedObject(self, getter, object, associationPolicy); 237 | }); 238 | 239 | class_addMethod(class, getter, getterImplementation, "@@:"); 240 | class_addMethod(class, setter, setterImplementation, "v@:@"); 241 | 242 | return; 243 | } 244 | 245 | objc_AssociationPolicy associationPolicy = isNonatomic ? OBJC_ASSOCIATION_RETAIN_NONATOMIC : OBJC_ASSOCIATION_RETAIN; 246 | 247 | #define CASE(type, selectorpart) if (encoding.UTF8String[0] == @encode(type)[0]) {\ 248 | IMP getterImplementation = imp_implementationWithBlock(^type(id self) {\ 249 | return [objc_getAssociatedObject(self, getter) selectorpart##Value];\ 250 | });\ 251 | \ 252 | IMP setterImplementation = imp_implementationWithBlock(^(id self, type object) {\ 253 | objc_setAssociatedObject(self, getter, @(object), associationPolicy);\ 254 | });\ 255 | \ 256 | class_addMethod(class, getter, getterImplementation, "@@:");\ 257 | class_addMethod(class, setter, setterImplementation, "v@:@");\ 258 | \ 259 | return;\ 260 | } 261 | 262 | CASE(char, char); 263 | CASE(unsigned char, unsignedChar); 264 | CASE(short, short); 265 | CASE(unsigned short, unsignedShort); 266 | CASE(int, int); 267 | CASE(unsigned int, unsignedInt); 268 | CASE(long, long); 269 | CASE(unsigned long, unsignedLong); 270 | CASE(long long, longLong); 271 | CASE(unsigned long long, unsignedLongLong); 272 | CASE(float, float); 273 | CASE(double, double); 274 | CASE(BOOL, bool); 275 | 276 | #undef CASE 277 | 278 | NSCAssert(NO, @"encoding %@ in not supported", encoding); 279 | } 280 | -------------------------------------------------------------------------------- /CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A7620EDE15F359680016F414 /* CTBlockDescription.h in Headers */ = {isa = PBXBuildFile; fileRef = A7620EDC15F359680016F414 /* CTBlockDescription.h */; }; 11 | A7620EDF15F359680016F414 /* CTBlockDescription.m in Sources */ = {isa = PBXBuildFile; fileRef = A7620EDD15F359680016F414 /* CTBlockDescription.m */; }; 12 | A7620F2B15F394430016F414 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A7620F2A15F394430016F414 /* CoreGraphics.framework */; }; 13 | A76AC25B154BDACB00B93FEF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76AC25A154BDACB00B93FEF /* Foundation.framework */; }; 14 | A76AC261154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = A76AC260154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m */; }; 15 | A76AC269154BDACC00B93FEF /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76AC268154BDACC00B93FEF /* SenTestingKit.framework */; }; 16 | A76AC26C154BDACC00B93FEF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A76AC25A154BDACB00B93FEF /* Foundation.framework */; }; 17 | A76AC26F154BDACC00B93FEF /* libCTObjectiveCRuntimeAdditions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A76AC257154BDACB00B93FEF /* libCTObjectiveCRuntimeAdditions.a */; }; 18 | A76AC275154BDACC00B93FEF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A76AC273154BDACC00B93FEF /* InfoPlist.strings */; }; 19 | A76AC278154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A76AC277154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.m */; }; 20 | A76AC283154BE37C00B93FEF /* CTTestSwizzleClass.m in Sources */ = {isa = PBXBuildFile; fileRef = A76AC282154BE37C00B93FEF /* CTTestSwizzleClass.m */; }; 21 | A76AC2BD154BECFF00B93FEF /* CTTestSwizzleSubclass.m in Sources */ = {isa = PBXBuildFile; fileRef = A76AC2BC154BECFF00B93FEF /* CTTestSwizzleSubclass.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | A76AC26D154BDACC00B93FEF /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = A76AC24E154BDACB00B93FEF /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = A76AC256154BDACB00B93FEF; 30 | remoteInfo = CTObjectiveCRuntimeAdditions; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | A7620EDC15F359680016F414 /* CTBlockDescription.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTBlockDescription.h; sourceTree = ""; }; 36 | A7620EDD15F359680016F414 /* CTBlockDescription.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTBlockDescription.m; sourceTree = ""; }; 37 | A7620F2A15F394430016F414 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 38 | A76AC257154BDACB00B93FEF /* libCTObjectiveCRuntimeAdditions.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCTObjectiveCRuntimeAdditions.a; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | A76AC25A154BDACB00B93FEF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 40 | A76AC25E154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CTObjectiveCRuntimeAdditions-Prefix.pch"; sourceTree = ""; }; 41 | A76AC25F154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTObjectiveCRuntimeAdditions.h; sourceTree = ""; }; 42 | A76AC260154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTObjectiveCRuntimeAdditions.m; sourceTree = ""; }; 43 | A76AC267154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CTObjectiveCRuntimeAdditionsTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | A76AC268154BDACC00B93FEF /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; 45 | A76AC272154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CTObjectiveCRuntimeAdditionsTests-Info.plist"; sourceTree = ""; }; 46 | A76AC274154BDACC00B93FEF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 47 | A76AC276154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CTObjectiveCRuntimeAdditionsTests.h; sourceTree = ""; }; 48 | A76AC277154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CTObjectiveCRuntimeAdditionsTests.m; sourceTree = ""; }; 49 | A76AC281154BE37C00B93FEF /* CTTestSwizzleClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTTestSwizzleClass.h; sourceTree = ""; }; 50 | A76AC282154BE37C00B93FEF /* CTTestSwizzleClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTTestSwizzleClass.m; sourceTree = ""; }; 51 | A76AC2BB154BECFF00B93FEF /* CTTestSwizzleSubclass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CTTestSwizzleSubclass.h; sourceTree = ""; }; 52 | A76AC2BC154BECFF00B93FEF /* CTTestSwizzleSubclass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CTTestSwizzleSubclass.m; sourceTree = ""; }; 53 | /* End PBXFileReference section */ 54 | 55 | /* Begin PBXFrameworksBuildPhase section */ 56 | A76AC254154BDACB00B93FEF /* Frameworks */ = { 57 | isa = PBXFrameworksBuildPhase; 58 | buildActionMask = 2147483647; 59 | files = ( 60 | A76AC25B154BDACB00B93FEF /* Foundation.framework in Frameworks */, 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | A76AC263154BDACC00B93FEF /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | A7620F2B15F394430016F414 /* CoreGraphics.framework in Frameworks */, 69 | A76AC269154BDACC00B93FEF /* SenTestingKit.framework in Frameworks */, 70 | A76AC26C154BDACC00B93FEF /* Foundation.framework in Frameworks */, 71 | A76AC26F154BDACC00B93FEF /* libCTObjectiveCRuntimeAdditions.a in Frameworks */, 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | /* End PBXFrameworksBuildPhase section */ 76 | 77 | /* Begin PBXGroup section */ 78 | A76AC24C154BDACB00B93FEF = { 79 | isa = PBXGroup; 80 | children = ( 81 | A76AC25C154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */, 82 | A76AC270154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests */, 83 | A76AC259154BDACB00B93FEF /* Frameworks */, 84 | A76AC258154BDACB00B93FEF /* Products */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | A76AC258154BDACB00B93FEF /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | A76AC257154BDACB00B93FEF /* libCTObjectiveCRuntimeAdditions.a */, 92 | A76AC267154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.octest */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | A76AC259154BDACB00B93FEF /* Frameworks */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | A7620F2A15F394430016F414 /* CoreGraphics.framework */, 101 | A76AC25A154BDACB00B93FEF /* Foundation.framework */, 102 | A76AC268154BDACC00B93FEF /* SenTestingKit.framework */, 103 | ); 104 | name = Frameworks; 105 | sourceTree = ""; 106 | }; 107 | A76AC25C154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | A7620EDC15F359680016F414 /* CTBlockDescription.h */, 111 | A7620EDD15F359680016F414 /* CTBlockDescription.m */, 112 | A76AC25F154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.h */, 113 | A76AC260154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m */, 114 | A76AC25D154BDACB00B93FEF /* Supporting Files */, 115 | ); 116 | path = CTObjectiveCRuntimeAdditions; 117 | sourceTree = ""; 118 | }; 119 | A76AC25D154BDACB00B93FEF /* Supporting Files */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | A76AC25E154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions-Prefix.pch */, 123 | ); 124 | name = "Supporting Files"; 125 | sourceTree = ""; 126 | }; 127 | A76AC270154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | A76AC276154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.h */, 131 | A76AC277154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.m */, 132 | A76AC271154BDACC00B93FEF /* Supporting Files */, 133 | A76AC281154BE37C00B93FEF /* CTTestSwizzleClass.h */, 134 | A76AC282154BE37C00B93FEF /* CTTestSwizzleClass.m */, 135 | A76AC2BB154BECFF00B93FEF /* CTTestSwizzleSubclass.h */, 136 | A76AC2BC154BECFF00B93FEF /* CTTestSwizzleSubclass.m */, 137 | ); 138 | path = CTObjectiveCRuntimeAdditionsTests; 139 | sourceTree = ""; 140 | }; 141 | A76AC271154BDACC00B93FEF /* Supporting Files */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | A76AC272154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests-Info.plist */, 145 | A76AC273154BDACC00B93FEF /* InfoPlist.strings */, 146 | ); 147 | name = "Supporting Files"; 148 | sourceTree = ""; 149 | }; 150 | /* End PBXGroup section */ 151 | 152 | /* Begin PBXHeadersBuildPhase section */ 153 | A76AC255154BDACB00B93FEF /* Headers */ = { 154 | isa = PBXHeadersBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | A7620EDE15F359680016F414 /* CTBlockDescription.h in Headers */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXHeadersBuildPhase section */ 162 | 163 | /* Begin PBXNativeTarget section */ 164 | A76AC256154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */ = { 165 | isa = PBXNativeTarget; 166 | buildConfigurationList = A76AC27B154BDACC00B93FEF /* Build configuration list for PBXNativeTarget "CTObjectiveCRuntimeAdditions" */; 167 | buildPhases = ( 168 | A76AC253154BDACB00B93FEF /* Sources */, 169 | A76AC254154BDACB00B93FEF /* Frameworks */, 170 | A76AC255154BDACB00B93FEF /* Headers */, 171 | ); 172 | buildRules = ( 173 | ); 174 | dependencies = ( 175 | ); 176 | name = CTObjectiveCRuntimeAdditions; 177 | productName = CTObjectiveCRuntimeAdditions; 178 | productReference = A76AC257154BDACB00B93FEF /* libCTObjectiveCRuntimeAdditions.a */; 179 | productType = "com.apple.product-type.library.static"; 180 | }; 181 | A76AC266154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests */ = { 182 | isa = PBXNativeTarget; 183 | buildConfigurationList = A76AC27E154BDACC00B93FEF /* Build configuration list for PBXNativeTarget "CTObjectiveCRuntimeAdditionsTests" */; 184 | buildPhases = ( 185 | A76AC262154BDACC00B93FEF /* Sources */, 186 | A76AC263154BDACC00B93FEF /* Frameworks */, 187 | A76AC264154BDACC00B93FEF /* Resources */, 188 | A76AC265154BDACC00B93FEF /* ShellScript */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | A76AC26E154BDACC00B93FEF /* PBXTargetDependency */, 194 | ); 195 | name = CTObjectiveCRuntimeAdditionsTests; 196 | productName = CTObjectiveCRuntimeAdditionsTests; 197 | productReference = A76AC267154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.octest */; 198 | productType = "com.apple.product-type.bundle"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | A76AC24E154BDACB00B93FEF /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | CLASSPREFIX = CT; 207 | LastUpgradeCheck = 0500; 208 | ORGANIZATIONNAME = ebf; 209 | }; 210 | buildConfigurationList = A76AC251154BDACB00B93FEF /* Build configuration list for PBXProject "CTObjectiveCRuntimeAdditions" */; 211 | compatibilityVersion = "Xcode 3.2"; 212 | developmentRegion = English; 213 | hasScannedForEncodings = 0; 214 | knownRegions = ( 215 | en, 216 | ); 217 | mainGroup = A76AC24C154BDACB00B93FEF; 218 | productRefGroup = A76AC258154BDACB00B93FEF /* Products */; 219 | projectDirPath = ""; 220 | projectRoot = ""; 221 | targets = ( 222 | A76AC256154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */, 223 | A76AC266154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests */, 224 | ); 225 | }; 226 | /* End PBXProject section */ 227 | 228 | /* Begin PBXResourcesBuildPhase section */ 229 | A76AC264154BDACC00B93FEF /* Resources */ = { 230 | isa = PBXResourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | A76AC275154BDACC00B93FEF /* InfoPlist.strings in Resources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXResourcesBuildPhase section */ 238 | 239 | /* Begin PBXShellScriptBuildPhase section */ 240 | A76AC265154BDACC00B93FEF /* ShellScript */ = { 241 | isa = PBXShellScriptBuildPhase; 242 | buildActionMask = 2147483647; 243 | files = ( 244 | ); 245 | inputPaths = ( 246 | ); 247 | outputPaths = ( 248 | ); 249 | runOnlyForDeploymentPostprocessing = 0; 250 | shellPath = /bin/sh; 251 | shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; 252 | }; 253 | /* End PBXShellScriptBuildPhase section */ 254 | 255 | /* Begin PBXSourcesBuildPhase section */ 256 | A76AC253154BDACB00B93FEF /* Sources */ = { 257 | isa = PBXSourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | A76AC261154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions.m in Sources */, 261 | A7620EDF15F359680016F414 /* CTBlockDescription.m in Sources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | A76AC262154BDACC00B93FEF /* Sources */ = { 266 | isa = PBXSourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | A76AC278154BDACC00B93FEF /* CTObjectiveCRuntimeAdditionsTests.m in Sources */, 270 | A76AC283154BE37C00B93FEF /* CTTestSwizzleClass.m in Sources */, 271 | A76AC2BD154BECFF00B93FEF /* CTTestSwizzleSubclass.m in Sources */, 272 | ); 273 | runOnlyForDeploymentPostprocessing = 0; 274 | }; 275 | /* End PBXSourcesBuildPhase section */ 276 | 277 | /* Begin PBXTargetDependency section */ 278 | A76AC26E154BDACC00B93FEF /* PBXTargetDependency */ = { 279 | isa = PBXTargetDependency; 280 | target = A76AC256154BDACB00B93FEF /* CTObjectiveCRuntimeAdditions */; 281 | targetProxy = A76AC26D154BDACC00B93FEF /* PBXContainerItemProxy */; 282 | }; 283 | /* End PBXTargetDependency section */ 284 | 285 | /* Begin PBXVariantGroup section */ 286 | A76AC273154BDACC00B93FEF /* InfoPlist.strings */ = { 287 | isa = PBXVariantGroup; 288 | children = ( 289 | A76AC274154BDACC00B93FEF /* en */, 290 | ); 291 | name = InfoPlist.strings; 292 | sourceTree = ""; 293 | }; 294 | /* End PBXVariantGroup section */ 295 | 296 | /* Begin XCBuildConfiguration section */ 297 | A76AC279154BDACC00B93FEF /* Debug */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ALWAYS_SEARCH_USER_PATHS = NO; 301 | CLANG_ENABLE_OBJC_ARC = YES; 302 | CLANG_WARN_BOOL_CONVERSION = YES; 303 | CLANG_WARN_CONSTANT_CONVERSION = YES; 304 | CLANG_WARN_EMPTY_BODY = YES; 305 | CLANG_WARN_ENUM_CONVERSION = YES; 306 | CLANG_WARN_INT_CONVERSION = YES; 307 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 308 | COPY_PHASE_STRIP = NO; 309 | GCC_C_LANGUAGE_STANDARD = gnu99; 310 | GCC_DYNAMIC_NO_PIC = NO; 311 | GCC_OPTIMIZATION_LEVEL = 0; 312 | GCC_PREPROCESSOR_DEFINITIONS = ( 313 | "DEBUG=1", 314 | "$(inherited)", 315 | ); 316 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 317 | GCC_THUMB_SUPPORT = NO; 318 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 319 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 320 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 321 | GCC_WARN_UNDECLARED_SELECTOR = YES; 322 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 323 | GCC_WARN_UNUSED_FUNCTION = YES; 324 | GCC_WARN_UNUSED_VARIABLE = YES; 325 | HEADER_SEARCH_PATHS = ( 326 | "$(inherited)", 327 | "\"$(SRCROOT)/../libffi/ios/include\"", 328 | ); 329 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 330 | ONLY_ACTIVE_ARCH = NO; 331 | SDKROOT = iphoneos; 332 | }; 333 | name = Debug; 334 | }; 335 | A76AC27A154BDACC00B93FEF /* Release */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | ALWAYS_SEARCH_USER_PATHS = NO; 339 | CLANG_ENABLE_OBJC_ARC = YES; 340 | CLANG_WARN_BOOL_CONVERSION = YES; 341 | CLANG_WARN_CONSTANT_CONVERSION = YES; 342 | CLANG_WARN_EMPTY_BODY = YES; 343 | CLANG_WARN_ENUM_CONVERSION = YES; 344 | CLANG_WARN_INT_CONVERSION = YES; 345 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 346 | COPY_PHASE_STRIP = YES; 347 | GCC_C_LANGUAGE_STANDARD = gnu99; 348 | GCC_THUMB_SUPPORT = NO; 349 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | HEADER_SEARCH_PATHS = ( 357 | "$(inherited)", 358 | "\"$(SRCROOT)/../libffi/ios/include\"", 359 | ); 360 | IPHONEOS_DEPLOYMENT_TARGET = 4.0; 361 | SDKROOT = iphoneos; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Release; 365 | }; 366 | A76AC27C154BDACC00B93FEF /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | DSTROOT = /tmp/CTObjectiveCRuntimeAdditions.dst; 370 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 371 | GCC_PREFIX_HEADER = "CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions-Prefix.pch"; 372 | GCC_THUMB_SUPPORT = NO; 373 | OTHER_LDFLAGS = "-ObjC"; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | SKIP_INSTALL = YES; 376 | }; 377 | name = Debug; 378 | }; 379 | A76AC27D154BDACC00B93FEF /* Release */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | DSTROOT = /tmp/CTObjectiveCRuntimeAdditions.dst; 383 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 384 | GCC_PREFIX_HEADER = "CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions-Prefix.pch"; 385 | GCC_THUMB_SUPPORT = NO; 386 | OTHER_LDFLAGS = "-ObjC"; 387 | PRODUCT_NAME = "$(TARGET_NAME)"; 388 | SKIP_INSTALL = YES; 389 | }; 390 | name = Release; 391 | }; 392 | A76AC27F154BDACC00B93FEF /* Debug */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | FRAMEWORK_SEARCH_PATHS = ( 396 | "$(SDKROOT)/Developer/Library/Frameworks", 397 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks", 398 | ); 399 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 400 | GCC_PREFIX_HEADER = "CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions-Prefix.pch"; 401 | GCC_THUMB_SUPPORT = NO; 402 | INFOPLIST_FILE = "CTObjectiveCRuntimeAdditionsTests/CTObjectiveCRuntimeAdditionsTests-Info.plist"; 403 | PRODUCT_NAME = "$(TARGET_NAME)"; 404 | WRAPPER_EXTENSION = octest; 405 | }; 406 | name = Debug; 407 | }; 408 | A76AC280154BDACC00B93FEF /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | FRAMEWORK_SEARCH_PATHS = ( 412 | "$(SDKROOT)/Developer/Library/Frameworks", 413 | "$(DEVELOPER_LIBRARY_DIR)/Frameworks", 414 | ); 415 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 416 | GCC_PREFIX_HEADER = "CTObjectiveCRuntimeAdditions/CTObjectiveCRuntimeAdditions-Prefix.pch"; 417 | GCC_THUMB_SUPPORT = NO; 418 | INFOPLIST_FILE = "CTObjectiveCRuntimeAdditionsTests/CTObjectiveCRuntimeAdditionsTests-Info.plist"; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | WRAPPER_EXTENSION = octest; 421 | }; 422 | name = Release; 423 | }; 424 | /* End XCBuildConfiguration section */ 425 | 426 | /* Begin XCConfigurationList section */ 427 | A76AC251154BDACB00B93FEF /* Build configuration list for PBXProject "CTObjectiveCRuntimeAdditions" */ = { 428 | isa = XCConfigurationList; 429 | buildConfigurations = ( 430 | A76AC279154BDACC00B93FEF /* Debug */, 431 | A76AC27A154BDACC00B93FEF /* Release */, 432 | ); 433 | defaultConfigurationIsVisible = 0; 434 | defaultConfigurationName = Release; 435 | }; 436 | A76AC27B154BDACC00B93FEF /* Build configuration list for PBXNativeTarget "CTObjectiveCRuntimeAdditions" */ = { 437 | isa = XCConfigurationList; 438 | buildConfigurations = ( 439 | A76AC27C154BDACC00B93FEF /* Debug */, 440 | A76AC27D154BDACC00B93FEF /* Release */, 441 | ); 442 | defaultConfigurationIsVisible = 0; 443 | defaultConfigurationName = Release; 444 | }; 445 | A76AC27E154BDACC00B93FEF /* Build configuration list for PBXNativeTarget "CTObjectiveCRuntimeAdditionsTests" */ = { 446 | isa = XCConfigurationList; 447 | buildConfigurations = ( 448 | A76AC27F154BDACC00B93FEF /* Debug */, 449 | A76AC280154BDACC00B93FEF /* Release */, 450 | ); 451 | defaultConfigurationIsVisible = 0; 452 | defaultConfigurationName = Release; 453 | }; 454 | /* End XCConfigurationList section */ 455 | }; 456 | rootObject = A76AC24E154BDACB00B93FEF /* Project object */; 457 | } 458 | --------------------------------------------------------------------------------