├── ObjCRuntimeDemo.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── lizelu.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── ObjCRuntimeDemo.xcscmblueprint
├── xcuserdata
│ └── lizelu.xcuserdatad
│ │ ├── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── ObjCRuntimeDemo.xcscheme
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
└── project.pbxproj
├── ObjCRuntimeDemo
├── TestClass
│ ├── TestClass+Category.h
│ ├── TestClass+SwapMethod.h
│ ├── TestClass+AssociatedObject.h
│ ├── TestClass+Category.m
│ ├── TestClass.h
│ ├── TestClass+SwapMethod.m
│ ├── TestClass+AssociatedObject.m
│ └── TestClass.m
├── Runtime
│ ├── RuntimeKit.h
│ └── RuntimeKit.m
└── main.m
└── README.md
/ObjCRuntimeDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo.xcodeproj/project.xcworkspace/xcuserdata/lizelu.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lizelu/ObjCRuntimeDemo/HEAD/ObjCRuntimeDemo.xcodeproj/project.xcworkspace/xcuserdata/lizelu.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass+Category.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass+Category.h
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass.h"
10 |
11 | @interface TestClass (Category)
12 | - (void)categoryMethod;
13 | @end
14 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass+SwapMethod.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass+SwapMethod.h
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/5.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass.h"
10 |
11 | @interface TestClass (SwapMethod)
12 | - (void)swapMethod;
13 | - (void)method2;
14 | @end
15 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass+AssociatedObject.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass+AssociatedObject.h
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/5.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass.h"
10 | #import "RuntimeKit.h"
11 | @interface TestClass (AssociatedObject)
12 | @property (nonatomic, strong) NSString *dynamicAddProperty;
13 | @end
14 |
15 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass+Category.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass+Category.m
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass+Category.h"
10 | #import "RuntimeKit.h"
11 |
12 | @implementation TestClass (Category)
13 | - (void)categoryMethod {
14 | NSLog(@"categoryMethod");
15 | }
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass.h:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass.h
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface TestClass : NSObject
12 | @property (nonatomic, strong) NSArray *publicProperty1;
13 | @property (nonatomic, strong) NSString *publicProperty2;
14 |
15 | + (void)classMethod: (NSString *)value;
16 | - (void)publicTestMethod1: (NSString *)value1 Second: (NSString *)value2;
17 | - (void)publicTestMethod2;
18 |
19 | - (void)method1;
20 | @end
21 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo.xcodeproj/xcuserdata/lizelu.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | ObjCRuntimeDemo.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | F482C9191E1CEB65009021C7
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass+SwapMethod.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass+SwapMethod.m
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/5.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass+SwapMethod.h"
10 | #import "RuntimeKit.h"
11 | @implementation TestClass (SwapMethod)
12 |
13 | - (void)swapMethod {
14 | [RuntimeKit methodSwap:[self class]
15 | firstMethod:@selector(method1)
16 | secondMethod:@selector(method2)];
17 | }
18 |
19 | - (void)method2 {
20 | //下方调用的实际上是method1的实现
21 | [self method2];
22 | NSLog(@"可以在Method1的基础上添加各种东西了");
23 | }
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass+AssociatedObject.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass+AssociatedObject.m
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/5.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass+AssociatedObject.h"
10 | #pragma mark - 动态属性关联
11 | static char kDynamicAddProperty;
12 | @implementation TestClass (AssociatedObject)
13 | /**
14 | getter方法
15 |
16 | @return 返回关联属性的值
17 | */
18 | - (NSString *)dynamicAddProperty {
19 | return objc_getAssociatedObject(self, &kDynamicAddProperty);
20 | }
21 |
22 |
23 | /**
24 | setter方法
25 |
26 | @param dynamicAddProperty 设置关联属性的值
27 | */
28 | - (void)setDynamicAddProperty:(NSString *)dynamicAddProperty {
29 | objc_setAssociatedObject(self, &kDynamicAddProperty, dynamicAddProperty, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
30 | }
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo.xcodeproj/xcuserdata/lizelu.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
8 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/Runtime/RuntimeKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // RuntimeKit.h
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 |
13 | @interface RuntimeKit : NSObject
14 | /**
15 | 获取类名
16 |
17 | @param class 相应类
18 | @return NSString:类名
19 | */
20 | + (NSString *)fetchClassName:(Class)class;
21 |
22 | /**
23 | 获取成员变量
24 |
25 | @param class 成员变量所在的类
26 | @return 返回成员变量字符串数组
27 | */
28 | + (NSArray *)fetchIvarList:(Class)class;
29 |
30 | /**
31 | 获取类的属性列表, 包括私有和公有属性,即定义在延展中的属性
32 |
33 | @param class Class
34 | @return 属性列表数组
35 | */
36 | + (NSArray *)fetchPropertyList:(Class)class;
37 |
38 | /**
39 | 获取对象方法列表:getter, setter, 对象方法等。但不能获取类方法
40 |
41 | @param class 方法所在的类
42 | @return 该类的方法列表
43 | */
44 | + (NSArray *)fetchMethodList:(Class)class;
45 |
46 | /**
47 | 获取协议列表
48 |
49 | @param class 实现协议的类
50 | @return 返回该类实现的协议列表
51 | */
52 | + (NSArray *)fetchProtocolList:(Class)class;
53 |
54 |
55 | /**
56 | 添加新的方法
57 |
58 | @param class 被添加方法的类
59 | @param methodSel SEL
60 | @param methodSelImpl 提供IMP的SEL
61 | */
62 | + (void)addMethod: (Class)class method:(SEL)methodSel method:(SEL)methodSelImpl;
63 |
64 | /**
65 | 方法交换
66 |
67 | @param class 交换方法所在的类
68 | @param method1 方法1
69 | @param method2 方法2
70 | */
71 | + (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2;
72 | @end
73 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "TestClass.h"
11 | #import "TestClass+Category.h"
12 | #import "TestClass+SwapMethod.h"
13 |
14 | #import "TestClass+AssociatedObject.h"
15 |
16 | #import "RuntimeKit.h"
17 | int main(int argc, const char * argv[]) {
18 | @autoreleasepool {
19 |
20 | NSString *className = [RuntimeKit fetchClassName:[TestClass class]];
21 | NSLog(@"测试类的类名为:%@\n", className);
22 |
23 | NSArray *ivarList = [RuntimeKit fetchIvarList:[TestClass class]];
24 | NSLog(@"\n获取TestClass的成员变量列表:%@", ivarList);
25 |
26 | NSArray *propertyList = [RuntimeKit fetchPropertyList:[TestClass class]];
27 | NSLog(@"\n获取TestClass的属性列表:%@", propertyList);
28 |
29 | NSArray *methodList = [RuntimeKit fetchMethodList:[TestClass class]];
30 | NSLog(@"\n获取TestClass的方法列表:%@", methodList);
31 |
32 | NSArray *protocolList = [RuntimeKit fetchProtocolList:[TestClass class]];
33 | NSLog(@"\n获取TestClass的协议列表:%@", protocolList);
34 | TestClass *instance = [TestClass new];
35 | [instance publicTestMethod2];
36 | [instance performSelector:@selector(noThisMethod:) withObject:@"实例方法参数"];
37 |
38 | instance.dynamicAddProperty = @"我是动态添加的属性";
39 | NSLog(@"%@", instance.dynamicAddProperty);
40 |
41 |
42 | [instance swapMethod];
43 | [instance method1];
44 |
45 | }
46 | return 0;
47 | }
48 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo.xcodeproj/project.xcworkspace/xcshareddata/ObjCRuntimeDemo.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "5D47AB54832BAE701C958946CDAAC540BA9F6253",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "5D47AB54832BAE701C958946CDAAC540BA9F6253" : 9223372036854775807,
8 | "59B0DD3A0DC0B86D2A06DD4B68CBD1C997876614" : 9223372036854775807
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "BDEA3AA8-9476-4623-81B4-BD060E146595",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "5D47AB54832BAE701C958946CDAAC540BA9F6253" : "ObjCRuntimeDemo\/",
13 | "59B0DD3A0DC0B86D2A06DD4B68CBD1C997876614" : ""
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "ObjCRuntimeDemo",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "ObjCRuntimeDemo.xcodeproj",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/lizelu\/CollectionViewControllerDemo.git",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "59B0DD3A0DC0B86D2A06DD4B68CBD1C997876614"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/lizelu\/ObjCRuntimeDemo.git",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "5D47AB54832BAE701C958946CDAAC540BA9F6253"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/TestClass/TestClass.m:
--------------------------------------------------------------------------------
1 | //
2 | // TestClass.m
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "TestClass.h"
10 | #import "RuntimeKit.h"
11 |
12 | @interface SecondClass : NSObject
13 | - (void)noThisMethod:(NSString *)value;
14 | @end
15 |
16 | @implementation SecondClass
17 | - (void)noThisMethod:(NSString *)value {
18 | NSLog(@"SecondClass中的方法实现%@", value);
19 | }
20 | @end
21 |
22 |
23 | @interface TestClass(){
24 | NSInteger _var1;
25 | int _var2;
26 | BOOL _var3;
27 | double _var4;
28 | float _var5;
29 | }
30 | @property (nonatomic, strong) NSMutableArray *privateProperty1;
31 | @property (nonatomic, strong) NSNumber *privateProperty2;
32 | @property (nonatomic, strong) NSDictionary *privateProperty3;
33 | @end
34 |
35 | @implementation TestClass
36 |
37 | + (void)classMethod: (NSString *)value {
38 | NSLog(@"publicTestMethod1");
39 | }
40 |
41 | - (void)publicTestMethod1: (NSString *)value1 Second: (NSString *)value2 {
42 | NSLog(@"publicTestMethod1");
43 | }
44 |
45 | - (void)publicTestMethod2 {
46 | NSLog(@"publicTestMethod2");
47 | }
48 |
49 | - (void)privateTestMethod1 {
50 | NSLog(@"privateTestMethod1");
51 | }
52 |
53 | - (void)privateTestMethod2 {
54 | NSLog(@"privateTestMethod2");
55 | }
56 |
57 | #pragma mark - 方法交换时使用
58 | - (void)method1 {
59 | NSLog(@"我是Method1的实现");
60 | }
61 |
62 | //运行时方法拦截
63 | - (void)dynamicAddMethod: (NSString *) value {
64 | NSLog(@"OC替换的方法:%@", value);
65 | }
66 |
67 | /**
68 | 没有找到SEL的IML实现时会执行下方的方法
69 |
70 | @param sel 当前对象调用并且找不到IML的SEL
71 | @return 找到其他的执行方法,并返回yes
72 | */
73 | + (BOOL)resolveInstanceMethod:(SEL)sel {
74 | return NO; //当返回NO时,会接着执行forwordingTargetForSelector:方法,
75 | [RuntimeKit addMethod:[self class] method:sel method:@selector(dynamicAddMethod:)];
76 | return YES;
77 | }
78 |
79 |
80 | /**
81 | 将当前对象不存在的SEL传给其他存在该SEL的对象
82 |
83 | @param aSelector 当前类中不存在的SEL
84 | @return 存在该SEL的对象
85 | */
86 | - (id)forwardingTargetForSelector:(SEL)aSelector {
87 | return self;
88 | return [SecondClass new]; //让SecondClass中相应的SEL去执行该方法
89 | }
90 |
91 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
92 | //查找父类的方法签名
93 | NSMethodSignature *signature = [super methodSignatureForSelector:selector];
94 | if(signature == nil) {
95 | signature = [NSMethodSignature signatureWithObjCTypes:"@@:"];
96 |
97 | }
98 | return signature;
99 | }
100 |
101 | - (void)forwardInvocation:(NSInvocation *)invocation {
102 | SecondClass * forwardClass = [SecondClass new];
103 | SEL sel = invocation.selector;
104 | if ([forwardClass respondsToSelector:sel]) {
105 | [invocation invokeWithTarget:forwardClass];
106 | } else {
107 | [self doesNotRecognizeSelector:sel];
108 | }
109 | }
110 |
111 | @end
112 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo.xcodeproj/xcuserdata/lizelu.xcuserdatad/xcschemes/ObjCRuntimeDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo/Runtime/RuntimeKit.m:
--------------------------------------------------------------------------------
1 | //
2 | // RuntimeKit.m
3 | // ObjCRuntimeDemo
4 | //
5 | // Created by Mr.LuDashi on 2017/1/4.
6 | // Copyright © 2017年 ZeluLi. All rights reserved.
7 | //
8 |
9 | #import "RuntimeKit.h"
10 |
11 | @implementation RuntimeKit
12 |
13 | /**
14 | 获取类名
15 |
16 | @param class 相应类
17 | @return NSString:类名
18 | */
19 | + (NSString *)fetchClassName:(Class)class {
20 | const char *className = class_getName(class);
21 | return [NSString stringWithUTF8String:className];
22 | }
23 |
24 | /**
25 | 获取成员变量
26 |
27 | @param class Class
28 | @return NSArray
29 | */
30 | + (NSArray *)fetchIvarList:(Class)class {
31 | unsigned int count = 0;
32 | Ivar *ivarList = class_copyIvarList(class, &count);
33 |
34 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
35 | for (unsigned int i = 0; i < count; i++ ) {
36 | NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:2];
37 | const char *ivarName = ivar_getName(ivarList[i]);
38 | const char *ivarType = ivar_getTypeEncoding(ivarList[i]);
39 | dic[@"type"] = [NSString stringWithUTF8String: ivarType];
40 | dic[@"ivarName"] = [NSString stringWithUTF8String: ivarName];
41 |
42 | [mutableList addObject:dic];
43 | }
44 | free(ivarList);
45 | return [NSArray arrayWithArray:mutableList];
46 | }
47 |
48 | /**
49 | 获取类的属性列表, 包括私有和公有属性,以及定义在延展中的属性
50 |
51 | @param class Class
52 | @return 属性列表数组
53 | */
54 | + (NSArray *)fetchPropertyList:(Class)class {
55 | unsigned int count = 0;
56 | objc_property_t *propertyList = class_copyPropertyList(class, &count);
57 |
58 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
59 | for (unsigned int i = 0; i < count; i++ ) {
60 | const char *propertyName = property_getName(propertyList[i]);
61 | [mutableList addObject:[NSString stringWithUTF8String: propertyName]];
62 | }
63 | free(propertyList);
64 | return [NSArray arrayWithArray:mutableList];
65 | }
66 |
67 |
68 | /**
69 | 获取类的实例方法列表:getter, setter, 对象方法等。但不能获取类方法
70 |
71 | @param class <#class description#>
72 | @return <#return value description#>
73 | */
74 | + (NSArray *)fetchMethodList:(Class)class {
75 | unsigned int count = 0;
76 | Method *methodList = class_copyMethodList(class, &count);
77 |
78 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
79 | for (unsigned int i = 0; i < count; i++ ) {
80 | Method method = methodList[i];
81 | SEL methodName = method_getName(method);
82 | [mutableList addObject:NSStringFromSelector(methodName)];
83 | }
84 | free(methodList);
85 | return [NSArray arrayWithArray:mutableList];
86 | }
87 |
88 | /**
89 | 获取协议列表
90 |
91 | @param class <#class description#>
92 | @return <#return value description#>
93 | */
94 | + (NSArray *)fetchProtocolList:(Class)class {
95 | unsigned int count = 0;
96 | __unsafe_unretained Protocol **protocolList = class_copyProtocolList(class, &count);
97 |
98 | NSMutableArray *mutableList = [NSMutableArray arrayWithCapacity:count];
99 | for (unsigned int i = 0; i < count; i++ ) {
100 | Protocol *protocol = protocolList[i];
101 | const char *protocolName = protocol_getName(protocol);
102 | [mutableList addObject:[NSString stringWithUTF8String: protocolName]];
103 | }
104 |
105 | return [NSArray arrayWithArray:mutableList];
106 | return nil;
107 | }
108 |
109 |
110 | /**
111 | 往类上添加新的方法与其实现
112 |
113 | @param class 相应的类
114 | @param methodSel 方法的名
115 | @param methodSelImpl 对应方法实现的方法名
116 | */
117 | + (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl {
118 | Method method = class_getInstanceMethod(class, methodSelImpl);
119 | IMP methodIMP = method_getImplementation(method);
120 | const char *types = method_getTypeEncoding(method);
121 | class_addMethod(class, methodSel, methodIMP, types);
122 | }
123 |
124 | /**
125 | 方法交换
126 |
127 | @param class 交换方法所在的类
128 | @param method1 方法1
129 | @param method2 方法2
130 | */
131 | + (void)methodSwap:(Class)class firstMethod:(SEL)method1 secondMethod:(SEL)method2 {
132 | Method firstMethod = class_getInstanceMethod(class, method1);
133 | Method secondMethod = class_getInstanceMethod(class, method2);
134 | method_exchangeImplementations(firstMethod, secondMethod);
135 | }
136 | @end
137 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # iOS开发之Runtime常用示例总结
2 | 经常有小伙伴私下在Q上问一些关于Runtime的东西,问我有没有Runtime的相关博客,之前还真没正儿八经的总结过。之前只是在解析第三方框架源码时,聊过一些用法,也就是这些第三方框架中用到的Runtime。比如属性关联,动态获取属性等等。本篇博客就针对Runtime这个主题来总结一些其常用的一些方法,当然“空谈误国”,今天博客中所聊的Runtime依然要依托于本篇博客所涉及的Demo。
3 |
4 | 本篇博客所聊的Runtime的内容大概有:动态获取类名、动态获取类的成员变量、动态获取类的属性列表、动态获取类的方法列表、动态获取类所遵循的协议列表、动态添加新的方法、类的实例方法实现的交换、动态属性关联、消息发送与消息转发机制等。当然,本篇博客总结的是运行时常用的功能,并不是所有Runtime的内容。
5 |
6 | ### 一、构建Runtime测试用例
7 | 本篇博客的内容是依托于实例的,所以我们在本篇博客中先构建我们的测试类,Runtime将会对该类进行相关的操作。下方就是本篇博客所涉及Demo的目录,上面的RuntimeKit类是讲Runtime常用的功能进行了简单的封装,而下方的TestClass以及相关的类目就是我们Runtime要操作的对象了。下方会对TestClass以及类目中的内容进行详细介绍。
8 | 
9 |
10 | 下方这几个截图就是我们的测试类TestClass的主要部分,因为TestClass是专门用来测试的类,所以其涉及的内容要尽量的全面。TestClass遵循了NSCoding, NSCopying这两个协议,并且为其添加了公有属性、私有属性、私有成员变量、 公有实例方法、私有实例方法、类方法等。这些添加的内容,都将是我们Runtime的操作对象。下方那几个TestClass的类目稍后在使用Runtime时再进行介绍。
11 | 
12 | 
13 | 
14 |
15 |
16 | ### 二、RuntimeKit的封装
17 |
18 | 接下来我们就来看看RuntimeKit中的内容,其中对Runtime常用的方法进行了简单的封装。主要是动态的获取类的一些属性和方法的,以及动态方法添加和方法交换的。本部分的干货还是不少的。
19 |
20 | #### 1、获取类名
21 | 动态的获取类名是比较简单的,使用class_getName(Class)就可以在运行时来获取类的名称。class_getName()函数返回的是一个char类型的指针,也就是C语言的字符串类型,所以我们要将其转换成NSString类型,然后再返回出去。下方的+fetchClassName:方法就是我们封装的获取类名的方法,如下所示:
22 | 
23 |
24 | #### 2、获取成员变量
25 | 下方这个+fetchIvarList:这个方法就是我们封装的获取类的成员变量的方法。当然我们在获取成员变量时,可以用ivar_getTypeEncoding()来获取相应成员变量的类型。使用ivar_getName()来获取相应成员变量的名称。下方就是对获取成员变量的功能的封装。返回的是一个数组,数组的元素是一个字典,而字典中存储的就是相应成员变量的名称和类型。
26 | 
27 |
28 | 下方就是调用上述方法获取的TestClass类的成员变量。当然在运行时就没有什么私有和公有之分了,只要是成员变量就可以获取到。在OC中的给类添加成员属性其实就是添加了一个成员变量和getter以及setter方法。所以获取的成员列表中肯定带有成员属性,不过成员属性的名称前方添加了下划线来与成员属性进行区分。我们也可以获取成员变量的类型,下方的_var1是NSInteger类型,动态获取到的是q字母,其实是NSInteger的符号。而i就表示int类型,c表示Bool类型,d表示double类型,f则就表示float类型。当然这些基本类型都是由一个字母代替的,如果是引用类型的话,则直接就是一个字符串了,比如NSArray类型就是"@NSArray"。
29 | 
30 |
31 | #### 3.获取成员属性
32 |
33 | 上面获取的是类的成员变量,那么下方这个+fetchPropertyList:获取的就是成员属性。当然此刻获取的只包括成员属性,也就是那些有setter或者getter方法的成员变量。下方主要是使用了class_copyPropertyList(Class,&count)来获取的属性列表,然后通过for循环通过property_getName()来获取每个属性的名字。当然使用property_getName()获取到的名字依然是C语言的char类型的指针,所以我们还需要将其转换成NSString类型,然后放到数组中一并返回。如下所示:
34 | 
35 |
36 | 下方这个截图就是调用上述方法获取的TestClass的所有的属性,当然dynamicAddProperty是我们使用Runtime动态给TestClass添加的,所以也是可以获取到的。当然我们获取到的属性的名称为了与其对应的成员变量进行区分,成员属性的名字前边是没有下划线的。
37 | 
38 |
39 | #### 4、获取类的实例方法
40 |
41 | 接下来我们就来封装一下获取类的实例方法列表的功能,下方这个+fetchMethodList:就是我们封装的获取类的实例方法列表的函数。在下方函数中,通过class_copyMethodList()方法获取类的实例方法列表,然后通过for循环使用method_getName()来获取每个方法的名称,然后将方法的名称转换成NSString类型,存储到数组中一并返回。具体代码如下所示:
42 | 
43 |
44 | 下方这个截图就是上述方法在TestClass上运行的结果,其中打印了TestClass类的所有实例方法,当然其中也必须得包含成员属性的getter和setter方法。当然TestClass类目中的方法也是必须能获取到的。结果如下所示:
45 | 
46 |
47 | #### 5、获取协议列表
48 |
49 | 下方是获取我们类所遵循协议列表的方法,主要使用了class_copyProtocolList()来获取列表,然后通过for循序使用protocol_getName()来获取协议的名称,最后将其转换成NSString类型放入数组中返回即可。
50 | 
51 |
52 | 下方就是我们获取到的TestClass类所遵循的协议列表:
53 | 
54 |
55 | #### 6、动态添加方法实现
56 |
57 | 下方就是动态的往相应类上添加方法以及实现。下方的+addMethod方法有三个参数,第一个参数是要添加方法的类,第二个参数是方法的SEL,第三个参数则是提供方法实现的SEL。稍后在消息发送和消息转发时会用到下方的方法。下方主要是使用class_getInstanceMethod()和method_getImplementation()这两个方法相结合获取相应SEL的方法实现。下方的IMP其实就是Implementation的方法缩写,获取到相应的方法实现后,然后再调用class_addMethod()方法将IMP与SEL进行绑定即可。具体做法如下所示。
58 | 
59 |
60 | #### 7、方法实现交换
61 |
62 | 下方就是讲类的两个方法的实现进行交换。如果将MethodA与MethodB的方法实现进行交换的话,调用MethodA时就会执行MethodB的内容,反之亦然。
63 | 
64 |
65 | 下方这段代码就是对上述方法的测试。下方是TestClass的一个类目,在该类目中将类目中的方法与TestClass中的方法进行了替换。也就是将method1与method2进行了替换,替换后在method2中调用的method2其实就是调用的method1。在第三方库中,经常会使用该特性,已达到AOP编程的目的。
66 | 
67 |
68 |
69 | ### 三、属性关联
70 |
71 | 属性关联说白了就是在类目中动态的为我们的类添加相应的属性,如果看过之前发布的对Masonry框架源码解析的博客的话,对下方的属性关联并不陌生。在Masonry框架中就利用Runtime的属性关联在UIView的类目中给UIView添加了一个约束数组,用来记录添加在当前View上的所有约束。下方就是在TestClass的类目中通过objc_getAssociatedObject()和objc_setAssociatedObject()两个方法为TestClass类添加了一个dynamicAddProperty属性。上面我们获取到的属性列表中就含有该动态添加的成员属性。
72 |
73 | 下方就是属性关联的具体代码,如下所示。
74 | 
75 |
76 |
77 |
78 | ### 四、消息处理与消息转发
79 |
80 | 在Runtime中不得不提的就是OC的消息处理和消息转发机制。当然网上也有不少相关资料,本篇博客为了完整性,还是要聊一下消息处理与消息转发的。当你调用一个类的方法时,先在本类中的方法缓存列表中进行查询,如果在缓存列表中找到了该方法的实现,就执行,如果找不到就在本类中的方列表中进行查找。在本类方列表中查找到相应的方法实现后就进行调用,如果没找到,就去父类中进行查找。如果在父类中的方法列表中找到了相应方法的实现,那么就执行,否则就执行下方的几步。
81 |
82 | 当调用一个方法在缓存列表,本类中的方法列表以及父类的方法列表找不到相应的实现时,到程序崩溃阶段中间还会有几步让你来挽救。接下来就来看看这几步该怎么走。
83 |
84 | #### 1.消息处理(Resolve Method)
85 |
86 | 当在相应的类以及父类中找不到类方法实现时会执行+resolveInstanceMethod:这个类方法。该方法如果在类中不被重写的话,默认返回NO。如果返回NO就表明不做任何处理,走下一步。如果返回YES的话,就说明在该方法中对这个找不到实现的方法进行了处理。在该方法中,我们可以为找不到实现的SEL动态的添加一个方法实现,添加完毕后,就会执行我们添加的方法实现。这样,当一个类调用不存在的方法时,就不会崩溃了。具体做法如下所示:
87 | 
88 |
89 | #### 2、消息快速转发
90 |
91 | 如果不对上述消息进行处理的话,也就是+resolveInstanceMethod:返回NO时,会走下一步消息转发,即-forwardingTargetForSelector:。该方法会返回一个类的对象,这个类的对象有SEL对应的实现,当调用这个找不到的方法时,就会被转发到SecondClass中去进行处理。这也就是所谓的消息转发。当该方法返回self或者nil, 说明不对相应的方法进行转发,那么就该走下一步了。
92 | 
93 |
94 | #### 3.消息常规转发
95 |
96 | 如果不将消息转发给其他类的对象,那么就只能自己进行处理了。如果上述方法返回self的话,会执行-methodSignatureForSelector:方法来获取方法的参数以及返回数据类型,也就是说该方法获取的是方法的签名并返回。如果上述方法返回nil的话,那么消息转发就结束,程序崩溃,报出找不到相应的方法实现的崩溃信息。
97 |
98 | 在+resolveInstanceMethod:返回NO时就会执行下方的方法,下方也是讲该方法转发给SecondClass,如下所示:
99 | 
100 |
--------------------------------------------------------------------------------
/ObjCRuntimeDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F482C91E1E1CEB66009021C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F482C91D1E1CEB66009021C7 /* main.m */; };
11 | F4ED27961E1CF1CE006126CA /* TestClass+Category.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27921E1CF1CE006126CA /* TestClass+Category.m */; };
12 | F4ED27971E1CF1CE006126CA /* TestClass.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27941E1CF1CE006126CA /* TestClass.m */; };
13 | F4ED279A1E1CF54F006126CA /* RuntimeKit.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27991E1CF54F006126CA /* RuntimeKit.m */; };
14 | F4ED27A21E1E2CA4006126CA /* TestClass+SwapMethod.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27A11E1E2CA4006126CA /* TestClass+SwapMethod.m */; };
15 | F4ED27A51E1E2D09006126CA /* TestClass+AssociatedObject.m in Sources */ = {isa = PBXBuildFile; fileRef = F4ED27A41E1E2D09006126CA /* TestClass+AssociatedObject.m */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | F482C9181E1CEB65009021C7 /* CopyFiles */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = /usr/share/man/man1/;
23 | dstSubfolderSpec = 0;
24 | files = (
25 | );
26 | runOnlyForDeploymentPostprocessing = 1;
27 | };
28 | /* End PBXCopyFilesBuildPhase section */
29 |
30 | /* Begin PBXFileReference section */
31 | F482C91A1E1CEB66009021C7 /* ObjCRuntimeDemo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ObjCRuntimeDemo; sourceTree = BUILT_PRODUCTS_DIR; };
32 | F482C91D1E1CEB66009021C7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
33 | F4ED27911E1CF1CE006126CA /* TestClass+Category.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestClass+Category.h"; sourceTree = ""; };
34 | F4ED27921E1CF1CE006126CA /* TestClass+Category.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TestClass+Category.m"; sourceTree = ""; };
35 | F4ED27931E1CF1CE006126CA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestClass.h; sourceTree = ""; };
36 | F4ED27941E1CF1CE006126CA /* TestClass.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestClass.m; sourceTree = ""; };
37 | F4ED27981E1CF54F006126CA /* RuntimeKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuntimeKit.h; sourceTree = ""; };
38 | F4ED27991E1CF54F006126CA /* RuntimeKit.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuntimeKit.m; sourceTree = ""; };
39 | F4ED27A01E1E2CA4006126CA /* TestClass+SwapMethod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestClass+SwapMethod.h"; sourceTree = ""; };
40 | F4ED27A11E1E2CA4006126CA /* TestClass+SwapMethod.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TestClass+SwapMethod.m"; sourceTree = ""; };
41 | F4ED27A31E1E2D09006126CA /* TestClass+AssociatedObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "TestClass+AssociatedObject.h"; sourceTree = ""; };
42 | F4ED27A41E1E2D09006126CA /* TestClass+AssociatedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "TestClass+AssociatedObject.m"; sourceTree = ""; };
43 | /* End PBXFileReference section */
44 |
45 | /* Begin PBXFrameworksBuildPhase section */
46 | F482C9171E1CEB65009021C7 /* Frameworks */ = {
47 | isa = PBXFrameworksBuildPhase;
48 | buildActionMask = 2147483647;
49 | files = (
50 | );
51 | runOnlyForDeploymentPostprocessing = 0;
52 | };
53 | /* End PBXFrameworksBuildPhase section */
54 |
55 | /* Begin PBXGroup section */
56 | F482C9111E1CEB65009021C7 = {
57 | isa = PBXGroup;
58 | children = (
59 | F482C91C1E1CEB66009021C7 /* ObjCRuntimeDemo */,
60 | F482C91B1E1CEB66009021C7 /* Products */,
61 | );
62 | sourceTree = "";
63 | };
64 | F482C91B1E1CEB66009021C7 /* Products */ = {
65 | isa = PBXGroup;
66 | children = (
67 | F482C91A1E1CEB66009021C7 /* ObjCRuntimeDemo */,
68 | );
69 | name = Products;
70 | sourceTree = "";
71 | };
72 | F482C91C1E1CEB66009021C7 /* ObjCRuntimeDemo */ = {
73 | isa = PBXGroup;
74 | children = (
75 | F4ED278F1E1CF1CE006126CA /* Runtime */,
76 | F4ED27901E1CF1CE006126CA /* TestClass */,
77 | F482C91D1E1CEB66009021C7 /* main.m */,
78 | );
79 | path = ObjCRuntimeDemo;
80 | sourceTree = "";
81 | };
82 | F4ED278F1E1CF1CE006126CA /* Runtime */ = {
83 | isa = PBXGroup;
84 | children = (
85 | F4ED27981E1CF54F006126CA /* RuntimeKit.h */,
86 | F4ED27991E1CF54F006126CA /* RuntimeKit.m */,
87 | );
88 | path = Runtime;
89 | sourceTree = "";
90 | };
91 | F4ED27901E1CF1CE006126CA /* TestClass */ = {
92 | isa = PBXGroup;
93 | children = (
94 | F4ED27931E1CF1CE006126CA /* TestClass.h */,
95 | F4ED27941E1CF1CE006126CA /* TestClass.m */,
96 | F4ED27911E1CF1CE006126CA /* TestClass+Category.h */,
97 | F4ED27921E1CF1CE006126CA /* TestClass+Category.m */,
98 | F4ED27A01E1E2CA4006126CA /* TestClass+SwapMethod.h */,
99 | F4ED27A11E1E2CA4006126CA /* TestClass+SwapMethod.m */,
100 | F4ED27A31E1E2D09006126CA /* TestClass+AssociatedObject.h */,
101 | F4ED27A41E1E2D09006126CA /* TestClass+AssociatedObject.m */,
102 | );
103 | path = TestClass;
104 | sourceTree = "";
105 | };
106 | /* End PBXGroup section */
107 |
108 | /* Begin PBXNativeTarget section */
109 | F482C9191E1CEB65009021C7 /* ObjCRuntimeDemo */ = {
110 | isa = PBXNativeTarget;
111 | buildConfigurationList = F482C9211E1CEB66009021C7 /* Build configuration list for PBXNativeTarget "ObjCRuntimeDemo" */;
112 | buildPhases = (
113 | F482C9161E1CEB65009021C7 /* Sources */,
114 | F482C9171E1CEB65009021C7 /* Frameworks */,
115 | F482C9181E1CEB65009021C7 /* CopyFiles */,
116 | );
117 | buildRules = (
118 | );
119 | dependencies = (
120 | );
121 | name = ObjCRuntimeDemo;
122 | productName = ObjCRuntimeDemo;
123 | productReference = F482C91A1E1CEB66009021C7 /* ObjCRuntimeDemo */;
124 | productType = "com.apple.product-type.tool";
125 | };
126 | /* End PBXNativeTarget section */
127 |
128 | /* Begin PBXProject section */
129 | F482C9121E1CEB65009021C7 /* Project object */ = {
130 | isa = PBXProject;
131 | attributes = {
132 | LastUpgradeCheck = 0820;
133 | ORGANIZATIONNAME = ZeluLi;
134 | TargetAttributes = {
135 | F482C9191E1CEB65009021C7 = {
136 | CreatedOnToolsVersion = 8.2.1;
137 | DevelopmentTeam = AWN3A347S4;
138 | ProvisioningStyle = Automatic;
139 | };
140 | };
141 | };
142 | buildConfigurationList = F482C9151E1CEB65009021C7 /* Build configuration list for PBXProject "ObjCRuntimeDemo" */;
143 | compatibilityVersion = "Xcode 3.2";
144 | developmentRegion = English;
145 | hasScannedForEncodings = 0;
146 | knownRegions = (
147 | en,
148 | );
149 | mainGroup = F482C9111E1CEB65009021C7;
150 | productRefGroup = F482C91B1E1CEB66009021C7 /* Products */;
151 | projectDirPath = "";
152 | projectRoot = "";
153 | targets = (
154 | F482C9191E1CEB65009021C7 /* ObjCRuntimeDemo */,
155 | );
156 | };
157 | /* End PBXProject section */
158 |
159 | /* Begin PBXSourcesBuildPhase section */
160 | F482C9161E1CEB65009021C7 /* Sources */ = {
161 | isa = PBXSourcesBuildPhase;
162 | buildActionMask = 2147483647;
163 | files = (
164 | F4ED27971E1CF1CE006126CA /* TestClass.m in Sources */,
165 | F4ED27A51E1E2D09006126CA /* TestClass+AssociatedObject.m in Sources */,
166 | F4ED27961E1CF1CE006126CA /* TestClass+Category.m in Sources */,
167 | F482C91E1E1CEB66009021C7 /* main.m in Sources */,
168 | F4ED279A1E1CF54F006126CA /* RuntimeKit.m in Sources */,
169 | F4ED27A21E1E2CA4006126CA /* TestClass+SwapMethod.m in Sources */,
170 | );
171 | runOnlyForDeploymentPostprocessing = 0;
172 | };
173 | /* End PBXSourcesBuildPhase section */
174 |
175 | /* Begin XCBuildConfiguration section */
176 | F482C91F1E1CEB66009021C7 /* Debug */ = {
177 | isa = XCBuildConfiguration;
178 | buildSettings = {
179 | ALWAYS_SEARCH_USER_PATHS = NO;
180 | CLANG_ANALYZER_NONNULL = YES;
181 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
182 | CLANG_CXX_LIBRARY = "libc++";
183 | CLANG_ENABLE_MODULES = YES;
184 | CLANG_ENABLE_OBJC_ARC = YES;
185 | CLANG_WARN_BOOL_CONVERSION = YES;
186 | CLANG_WARN_CONSTANT_CONVERSION = YES;
187 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
188 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
189 | CLANG_WARN_EMPTY_BODY = YES;
190 | CLANG_WARN_ENUM_CONVERSION = YES;
191 | CLANG_WARN_INFINITE_RECURSION = YES;
192 | CLANG_WARN_INT_CONVERSION = YES;
193 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
194 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
195 | CLANG_WARN_UNREACHABLE_CODE = YES;
196 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
197 | CODE_SIGN_IDENTITY = "-";
198 | COPY_PHASE_STRIP = NO;
199 | DEBUG_INFORMATION_FORMAT = dwarf;
200 | ENABLE_STRICT_OBJC_MSGSEND = YES;
201 | ENABLE_TESTABILITY = YES;
202 | GCC_C_LANGUAGE_STANDARD = gnu99;
203 | GCC_DYNAMIC_NO_PIC = NO;
204 | GCC_NO_COMMON_BLOCKS = YES;
205 | GCC_OPTIMIZATION_LEVEL = 0;
206 | GCC_PREPROCESSOR_DEFINITIONS = (
207 | "DEBUG=1",
208 | "$(inherited)",
209 | );
210 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
211 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
212 | GCC_WARN_UNDECLARED_SELECTOR = YES;
213 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
214 | GCC_WARN_UNUSED_FUNCTION = YES;
215 | GCC_WARN_UNUSED_VARIABLE = YES;
216 | MACOSX_DEPLOYMENT_TARGET = 10.12;
217 | MTL_ENABLE_DEBUG_INFO = YES;
218 | ONLY_ACTIVE_ARCH = YES;
219 | SDKROOT = macosx;
220 | };
221 | name = Debug;
222 | };
223 | F482C9201E1CEB66009021C7 /* Release */ = {
224 | isa = XCBuildConfiguration;
225 | buildSettings = {
226 | ALWAYS_SEARCH_USER_PATHS = NO;
227 | CLANG_ANALYZER_NONNULL = YES;
228 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
229 | CLANG_CXX_LIBRARY = "libc++";
230 | CLANG_ENABLE_MODULES = YES;
231 | CLANG_ENABLE_OBJC_ARC = YES;
232 | CLANG_WARN_BOOL_CONVERSION = YES;
233 | CLANG_WARN_CONSTANT_CONVERSION = YES;
234 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
235 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
236 | CLANG_WARN_EMPTY_BODY = YES;
237 | CLANG_WARN_ENUM_CONVERSION = YES;
238 | CLANG_WARN_INFINITE_RECURSION = YES;
239 | CLANG_WARN_INT_CONVERSION = YES;
240 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
241 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
242 | CLANG_WARN_UNREACHABLE_CODE = YES;
243 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
244 | CODE_SIGN_IDENTITY = "-";
245 | COPY_PHASE_STRIP = NO;
246 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
247 | ENABLE_NS_ASSERTIONS = NO;
248 | ENABLE_STRICT_OBJC_MSGSEND = YES;
249 | GCC_C_LANGUAGE_STANDARD = gnu99;
250 | GCC_NO_COMMON_BLOCKS = YES;
251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
253 | GCC_WARN_UNDECLARED_SELECTOR = YES;
254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
255 | GCC_WARN_UNUSED_FUNCTION = YES;
256 | GCC_WARN_UNUSED_VARIABLE = YES;
257 | MACOSX_DEPLOYMENT_TARGET = 10.12;
258 | MTL_ENABLE_DEBUG_INFO = NO;
259 | SDKROOT = macosx;
260 | };
261 | name = Release;
262 | };
263 | F482C9221E1CEB66009021C7 /* Debug */ = {
264 | isa = XCBuildConfiguration;
265 | buildSettings = {
266 | DEVELOPMENT_TEAM = AWN3A347S4;
267 | PRODUCT_NAME = "$(TARGET_NAME)";
268 | };
269 | name = Debug;
270 | };
271 | F482C9231E1CEB66009021C7 /* Release */ = {
272 | isa = XCBuildConfiguration;
273 | buildSettings = {
274 | DEVELOPMENT_TEAM = AWN3A347S4;
275 | PRODUCT_NAME = "$(TARGET_NAME)";
276 | };
277 | name = Release;
278 | };
279 | /* End XCBuildConfiguration section */
280 |
281 | /* Begin XCConfigurationList section */
282 | F482C9151E1CEB65009021C7 /* Build configuration list for PBXProject "ObjCRuntimeDemo" */ = {
283 | isa = XCConfigurationList;
284 | buildConfigurations = (
285 | F482C91F1E1CEB66009021C7 /* Debug */,
286 | F482C9201E1CEB66009021C7 /* Release */,
287 | );
288 | defaultConfigurationIsVisible = 0;
289 | defaultConfigurationName = Release;
290 | };
291 | F482C9211E1CEB66009021C7 /* Build configuration list for PBXNativeTarget "ObjCRuntimeDemo" */ = {
292 | isa = XCConfigurationList;
293 | buildConfigurations = (
294 | F482C9221E1CEB66009021C7 /* Debug */,
295 | F482C9231E1CEB66009021C7 /* Release */,
296 | );
297 | defaultConfigurationIsVisible = 0;
298 | defaultConfigurationName = Release;
299 | };
300 | /* End XCConfigurationList section */
301 | };
302 | rootObject = F482C9121E1CEB65009021C7 /* Project object */;
303 | }
304 |
--------------------------------------------------------------------------------