主要包含 字典与模型互转 、对象关联、遍历变量、遍历方法、遍历协议
字典与模型互转
字典转模型的时候:
- 根据字典的 key 生成 setter 方法.
- 使用 objc_msgSend 调用 setter 方法为 Model 的属性赋值(或者 KVC).
模型转字典的时候:
- 调用 class_copyPropertyList 方法获取当前 Model 的所有属性.
- 调用 property_getName 获取属性名称.
- 根据属性名称生成 getter 方法.
- 使用 objc_msgSend 调用 getter 方法获取属性值(或者 KVC).
示例:
1 |
|
对象关联
对已经存在的类在 Category 中添加自定义的属性:
- 要取出被关联的对象使用 objc_getAssociatedObject 方法即可,
- 要删除一个被关联的对象,使用 objc_setAssociatedObject 方法将对应的 key 设置成 nil 即可
- objc_removeAssociatedObjects 方法将会移除源对象中所有的关联对象.
示例:
为类别添加chineseName属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@implementation XiaoMing (MutipleName)
char cName;
-(void)setChineseName:(NSString *) chineseName{
objc_setAssociatedObject(self, &cName, chineseName, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)chineseName{
return objc_getAssociatedObject(self, &cName);
}
@end
举个栗子,假如我们要给 UIButton 添加一个监听单击事件的 block 属性,新建 UIButton 的 Category,其.m文件如下: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
static const void *associatedKey = "associatedKey";
@implementation UIButton (ClickBlock)
//Category中的属性,只会生成setter和getter方法,不会生成成员变量
-(void)setClick:(clickBlock)click{
objc_setAssociatedObject(self, associatedKey, click, OBJC_ASSOCIATION_COPY_NONATOMIC);
[self removeTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];
if (click) {
[self addTarget:self action:@selector(buttonClick) forControlEvents:UIControlEventTouchUpInside];
}
}
-(clickBlock)click{
return objc_getAssociatedObject(self, associatedKey);
}
-(void)buttonClick{
if (self.click) {
self.click();
}
}
然后在代码中,就可以使用 UIButton 的属性来监听单击事件了:1
2
3
4
5
6
7
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = self.view.bounds;
[self.view addSubview:button];
button.click = ^{
NSLog(@"buttonClicked");
};
遍历变量
- 使用 class_copyIvarList 方法获取当前 Model 的所有成员变量.
- 使用 ivar_getName 方法获取成员变量的名称.
- 通过 KVC 来读取 Model 的属性值
示例:
1 |
|
自动归档
1 |
|
修改变量值
- 使用 class_copyIvarList 方法获取当前 Model 的所有成员变量.
- 使用 ivar_getName 方法获取成员变量的名称.
- 通过 KVC 来读取 Model 的属性值
- 修改对应的字段值 object_setIvar(id obj, Ivar ivar, id value)
示例
1 |
|
遍历属性
- 通过class_copyPropertyList函数来获取属性列表,其中属性列表是使用@property声明的列表,对于直接使用声明为成员变量的,都不会出现在属性列表中,这也是正常的。
- 我们通过runtime提供的property_getName函数来获取属性名称。
- 若有获取属性的详细描述,可通过runtime提供的property_getAttributes函数来获取。
- 若有获取属性中的objc_property_attribute_t列表,可以通过property_copyAttributeList函数来获取。
- 若有获取单独的objc_property_attribute_t的name或者value,直接使用点语法即可,它是一个结构体。
示例:
1 |
|
遍历方法
- 使用 class_copyMethodList 方法获取当前 Model 的所有成员变量.
- 使用 method_getName 方法获取成员变量的名称.
示例:
1 |
|
动态交换方法
Method class_getInstanceMethod(Class cls, SEL name)
找到方法实例void method_exchangeImplementations(Method m1, Method m2)
交换方法
示例
1 |
|
Method Swizzling 原理
归根结底,都是偷换了selector的IMP
- method_exchangeImplementations 来交换2个方法中的IMP,
- 利用 class_replaceMethod 来修改类,
- 利用 method_setImplementation 来直接设置某个方法的IMP
动态添加方法
void class_addMethods(Class, struct objc_method_list *)
添加多个方法BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)
添加方法- (IMP)guessAnswer 意思是guessAnswer的地址指针;
- “v@:” 意思是,v代表无返回值void,如果是i则代表int;@代表 id sel; : 代表 SEL _cmd;“v@:@@” 意思是,两个参数的没有返回值。
示例:
假设XiaoMing的中没有guess这个方法,后来被Runtime添加一个名字叫guess的方法,最终再调用guess方法做出相应。
1 |
|
调用:
`[self.xiaoMing performSelector:@selector(guess)];`
遍历协议
- 使用 class_copyProtocolList 方法获取当前 Model 的所有成员变量.
- 使用 protocol_getName 方法获取成员变量的名称.
示例:
1 |
|