ObjC的RunTime以及项目实践

文章出处,原创于 https://HawkingOuYang.github.io/

我的GitHub


ObjC runtime → OneV
Objective-C具有相当多的动态特性,基本的,也是经常被提到和用到的有动态类型(Dynamic typing),动态绑定(Dynamic binding)和动态加载(Dynamic loading)。

runtime 埋点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
#import <objc/runtime.h>
#import <objc/message.h>
typedef void (*_VIMP)(id,SEL,...);
typedef id (*_IMP)(id ,SEL,...);
#import "UIViewController+OYxjViewDidAppear.h"
#pragma mark - const
//! 关联属性 KEY pageUUID
static const char *kPropertyKey_pageUUID ="kPropertyKey_pageUUID";
#pragma mark - Class
@interface UIViewController ()
@property(nonatomic,copy) NSString *pageUUID;
@end
@implementation UIViewController (OYxjViewDidAppear)
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
{//页面显示--begin//
Method mViewDidAppear = class_getInstanceMethod(self, @selector(viewDidAppear:));
//获得方法实现
_VIMP mViewDidAppear_IMP = (_VIMP)method_getImplementation(mViewDidAppear);
//重新 实现方法
method_setImplementation(mViewDidAppear, imp_implementationWithBlock(^ (id target, SEL action){
NSTimeInterval timestamp = [[NSDate date] timeIntervalSince1970];
// 使用类名 作为 页面名字
NSString *vcName = NSStringFromClass([target class]);
// 过滤掉UIKit 的内部控制器,没有必要统计
BOOL isSelfInputVC = [vcName hasSuffix:@"InputViewController"] && ![vcName hasPrefix:@"HTm"];
BOOL isUIKitVC = isSelfInputVC ||
[vcName hasSuffix:@"InputWindowController"] ||
[vcName hasPrefix:@"PUUI"] ||
[vcName hasPrefix:@"CKSMSComposeController"] ||
[vcName hasPrefix:@"PUUIAlbumListViewController"];
if (
[target isKindOfClass:[UITabBarController class]]
||
[target isKindOfClass:[UINavigationController class]]
||
isUIKitVC
)
{
//do nothing here !
//tabbar控制器、导航栏控制器,不上报日志。
}
else if (
[target navigationController] == nil
||
[[[target navigationController] viewControllers] containsObject:target]
)//自己写的页面,才上报日志。
{
NSString *page_uuid = [NSString stringWithFormat:@"%@_%f",vcName,timestamp];
//关联对象
[target setPageUUID:page_uuid];
NSString *page_id =vcName;
[e1 addPropwithStr:@"pageId" propVal:page_id.length?page_id:@""];
[e1 addPropwithStr:@"page_uuid" propVal:page_uuid.length?page_uuid:@""];
DDLogVerbose(@"上报日志,页面显示:%@ %@, %@ %@", @"page_id",page_id, @"page_uuid",page_uuid );
}
// 调用原来的方法实现
mViewDidAppear_IMP(target,@selector(viewDidAppear:));
}));
}//页面显示--end//
{//页面消失--begin//
Method mViewWilDisappear = class_getInstanceMethod(self, @selector(viewWillDisappear:));
//获得方法实现
_VIMP mViewWillDisappear_IMP = (_VIMP)method_getImplementation(mViewWilDisappear);
//重新 实现方法
method_setImplementation(mViewWilDisappear, imp_implementationWithBlock(^ (id target, SEL action){
// 使用类名 作为 页面名字
NSString *vcName = NSStringFromClass([target class]);
// 过滤掉UIKit 的内部控制器,没有必要统计
BOOL isSelfInputVC = [vcName hasSuffix:@"InputViewController"] && ![vcName hasPrefix:@"HTm"];
BOOL isUIKitVC = isSelfInputVC ||
[vcName hasSuffix:@"InputWindowController"] ||
[vcName hasPrefix:@"PUUI"] ||
[vcName hasPrefix:@"CKSMSComposeController"] ||
[vcName hasPrefix:@"PUUIAlbumListViewController"];
if (
[target isKindOfClass:[UITabBarController class]]
||
[target isKindOfClass:[UINavigationController class]]
||
isUIKitVC
)
{
//do nothing here !
//tabbar控制器、导航栏控制器,不上报日志。
}
else if (
[target navigationController] == nil
||
[[[target navigationController] viewControllers] containsObject:target]
)//自己写的页面,才上报日志。
{
// 关联page_uuid
NSString *page_uuid = [target pageUUID];
NSString *page_id = vcName;
[e1 addPropwithStr:@"pageId" propVal:page_id.length?page_id:@""];
[e1 addPropwithStr:@"page_uuid" propVal:page_uuid.length?page_uuid:@""];
DDLogVerbose(@"上报日志,页面消失:%@ %@, %@ %@", @"page_id",page_id, @"page_uuid",page_uuid );
}
// 调用原来的方法实现
mViewWillDisappear_IMP(target,@selector(viewWillDisappear:));
}));
}//页面消失--end//
});//<--- dispatch_once END --->//
};
#pragma mark - 关联属性
//! 关联 pageUUID 这个property。
- (NSString *)pageUUID
{
return objc_getAssociatedObject(self,
kPropertyKey_pageUUID);
}
//! 关联 pageUUID 这个property。
- (void)setPageUUID:(NSString *)pageUUID
{
objc_setAssociatedObject(self,
kPropertyKey_pageUUID,
pageUUID,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

runtime 加载 .dylib