Runtime 实践

介绍

  • dynamicWebkit替换、
  • 动态添加方法 + 动态交换方法 -> UIButton防止多次点击、
  • 遍历变量 -> 隐藏键盘、
  • 遍历属性 -> 获取属性名
  • 遍历属性 -> 判断所有变量是否已经实例化

dynamicWebkit

Apple 从 iOS8 开始,推出了更新、优化更好的WKWebkit。这个库是UIWebView的继承者,在相同的浏览页面下,WKWebKit提供的WKWebView的内存占用率甚至可以只有UIWebView的1/10。可惜的是,我们很多时候为了保证用户的覆盖率,target iOS Version都是 iOS7。这时候我们就需要使用UIWebView来达到显示的目的。
那么问题来了,如何实现根据iOS版本来达到动态加载的目的呢?

参考 http://www.jianshu.com/p/3f71fb190fbe

感觉这更多是偏向于URL route的感觉,类似的还有:
http://www.jianshu.com/p/8b3a9155468d

动态添加方法 + 动态交换方法 -> UIButton防止多次点击

步骤:

  • 新建UIButton分类,在load方法中 使用自定义方法替换掉原先的 - (void)sendAction:(SEL)action to:(nullable id)target forEvent:(nullable UIEvent *)event方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ (void)load {
SEL originSEL = @selector(sendAction:to:forEvent:);
SEL mySEL = @selector(my_sendAction:to:forEvent:);

Method originM = class_getInstanceMethod([self class], originSEL);
const char *typeEncodinds = method_getTypeEncoding(originM);

Method newM = class_getInstanceMethod([self class], mySEL);
IMP newIMP = method_getImplementation(newM);

if (class_addMethod([self class], mySEL, newIMP, typeEncodinds)) {
class_replaceMethod([self class], originSEL, newIMP, typeEncodinds);
} else {
method_exchangeImplementations(originM, newM);
}
}
  • 实现自定义方法,在某个时间间隔之内,不响应点击
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
- (void)my_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {

// 保险起见,判断下Class类型
if ([self isKindOfClass:[UIButton class]]) {

//1. 按钮点击间隔事件
self.clickDurationTime = self.clickDurationTime == 0 ? defaultDuration : self.clickDurationTime;

//2. 是否忽略按钮点击事件
if (_isIgnoreEvent) {
NSLog(@" 忽略按钮事件 ");
return;
} else if(self.clickDurationTime > 0) {
NSLog(@"//2.2 不忽略按钮事件");

// 后续在间隔时间内直接忽略按钮事件
_isIgnoreEvent = YES;

// 间隔事件后,执行按钮事件
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.clickDurationTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
resetState();
});

// 发送按钮点击消息
[self my_sendAction:action to:target forEvent:event];
}

} else {
[self my_sendAction:action to:target forEvent:event];
}
}

遍历变量 -> 隐藏键盘

在viewcontroller中有些操作需要隐藏原先的键盘

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 找到 obj 实例中的 textfield 和 textView,并取消键盘
*
* @param obj (not nil)
*/
+(void)resignTFandTV:(id)obj{

unsigned int outCount = 0;
Ivar *vars = class_copyIvarList([obj class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar var = vars[i];
const char *name = ivar_getName(var);
NSString *key = [NSString stringWithUTF8String:name];

id value = [obj valueForKey:key];

if ([value isKindOfClass:[UITextField class]] || [value isKindOfClass:[UITextView class]]) {
if([value canResignFirstResponder])
[value resignFirstResponder];
}
}
}

遍历属性 -> 获取属性名

一些简单的model 或者自定义 view ,可以获取属性名来实例化对应的属性

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
/**
* 找到类定义 中的 属性名(如 _msString) 数组
(如 用来生成类型的成员变量)
* @param obj 类的实例
* @param aClass 需要获取的特定的类型的属性名 (若为空 则 寻找所有的属性名)
*
* @return 属性名返回顺序和obj中定义的顺序一致
*/
+(NSMutableArray<NSString*>*)getIvarNamesFrom:(id)obj specialCls:(Class)aClass{

NSMutableArray<NSString*>* arr = [NSMutableArray arrayWithCapacity:0];

unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList([obj class], &outCount);
for (int i = 0; i < outCount; i ++) {
objc_property_t property = properties[i];

NSString* name = [self getProertyIvarName:property];
NSString* type = [self getProertyTypeName:property];

if(aClass){
if(type){
Class someClass = NSClassFromString(type);
if(someClass && someClass == aClass ){
if(name) [arr addObject:name];
}
}
}
else{//若没有特定aclass 则取所有类型的值
if(name) [arr addObject:name];
}

}
free(properties);

return arr;
}
/* 用法:

-(void)setupLabels{

NSMutableArray<NSString*>* labelNames = [LQUtils getIvarNamesFrom:self specialCls:[UILabel class]];

for(int i = 0 ; i < labelNames.count; i++){
UILabel* tf = [UILabel new];
tf.font = [UIFont systemFontOfSize:14];
tf.textColor = [UIColor lightGrayColor];
tf.textAlignment = NSTextAlignmentLeft;
[self.contentView addSubview:tf];
[self setValue:tf forKeyPath:labelNames[i]];
}

}

*/



/**
* 找到 objc_property_t 结构中保存的 属性名 ; 如 _myString
*
* @param property
*
* @return
*/
+(NSString*)getProertyIvarName:(objc_property_t)property{

unsigned int count = 0;
objc_property_attribute_t *attrbutes = property_copyAttributeList(property, &count);
for (unsigned int j = 0; j < count; ++j) {
objc_property_attribute_t attribute = attrbutes[j];

const char *name = attribute.name;
const char *value = attribute.value;
NSString *utfName = [NSString stringWithUTF8String:name];
NSString *utfValue = [NSString stringWithUTF8String:value];

if([utfName isEqualToString:@"V"]){

utfValue = [utfValue stringByReplacingOccurrencesOfString:@"\"" withString:@""];
utfValue = [utfValue stringByReplacingOccurrencesOfString:@"@" withString:@""];
return utfValue;
}
}

free(attrbutes);

return nil;
}
/**
* 找到 objc_property_t 结构中保存的 属性类型名 ; 如 NSString
*
* @param property
*
* @return
*/
+(NSString*)getProertyTypeName:(objc_property_t)property{

unsigned int count = 0;
objc_property_attribute_t *attrbutes = property_copyAttributeList(property, &count);
for (unsigned int j = 0; j < count; ++j) {
objc_property_attribute_t attribute = attrbutes[j];

const char *name = attribute.name;
const char *value = attribute.value;
NSString *utfName = [NSString stringWithUTF8String:name];
NSString *utfValue = [NSString stringWithUTF8String:value];

if([utfName isEqualToString:@"T"]){

utfValue = [utfValue stringByReplacingOccurrencesOfString:@"\"" withString:@""];
utfValue = [utfValue stringByReplacingOccurrencesOfString:@"@" withString:@""];
return utfValue;
}
}

free(attrbutes);

return nil;
}

遍历属性 -> 判断所有变量是否已经实例化

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
/**
* 查看类的实例中 某些特定类型的 属性是否实例化
*
* @param obj
* @param aClass
*
* @return
*/
+(BOOL)isIvarsVaildOf:(id)obj specialCls:(Class)aClass{
NSMutableArray<NSString*>* ivarNames = [self getIvarNamesFrom:self specialCls:aClass];
for(int i = 0 ; i < ivarNames.count; i++){
NSString* key = ivarNames[i];
id value = [obj valueForKey:key];
if(!value) return false;
}
return true;

}
/**
* 查看类的实例中所有属性是否实例化
*/
+(BOOL)isIvarAllVaildOf:(id)obj{
return [self isIvarsVaildOf:obj specialCls:nil];

}