iOS Runtime学习(三) -- 简单使用篇
2019-03-19 本文已影响19人
Q海龙
一、前言
runtime功能很强大,本文简单的介绍几个实用的小功能,如动态添加属性
,动态添加方法
,方法替换
,字典转模型
的几个小例子,本文将对UILabel进行一些简单的操作。
创建工程,创建一个Objective-C File
File:
Test
File Type:Category
Class:UILabel
二、添加属性
对UILabel添加一个名为abc
的属性
- 在UILabel+Test.h文件中,添加如下代码
//添加一个abc属性
@property (nonatomic, strong) NSString *abc;
2.在UILabel+Test.m文件中,添加如下代码
//创建一个关联的key
static NSString *abcStr = @"abcStr";
添加对应的set和get方法
- (void)setAbc:(NSString *)abc {
objc_setAssociatedObject(self, &abcStr, abc, OBJC_ASSOCIATION_COPY);
}
- (NSString *)abc {
return objc_getAssociatedObject(self, &abcStr);
}
二、方法替换
我们将新添加的
- (void)setAbc:(NSString *)abc {
objc_setAssociatedObject(self, &abcStr, abc, OBJC_ASSOCIATION_COPY);
}
替换为
- (void)fu_setAbc:(NSString *)abc {
NSLog(@"这里是替换setAbc后的方法");
}
我们需要在+(void)load
方法中,将这个操作做好
+ (void)load {
//方法替换 : 将abc的set方法 替换成fu_setAbc
SEL oldSel = @selector(setAbc:);
SEL newSel = @selector(fu_setAbc:);
Method oldMethod = class_getInstanceMethod(self, oldSel);
Method newMethod = class_getInstanceMethod(self, newSel);
//将newSel添加到当前类中,如果当前类有同名的实现,则返回NO
BOOL boolean = class_addMethod(self, oldSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
if (boolean) {
class_replaceMethod(self, newSel, method_getImplementation(oldMethod), method_getTypeEncoding(oldMethod));
}else {
method_exchangeImplementations(oldMethod, newMethod);
}
}
三、动态添加方法
在这里需要使用到performSelector,它是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。
我们就用此方法来调用一下UILabel
的test
方法
[label performSelector:@selector(test:) withObject:@"test"];
在UILabel+Test.m文件中,添加对应的test
方法
void test (id self, SEL _cmd, NSString *str) {
NSLog(@"动态添加方法成功");
}
然后,我们在resolveInstanceMethod
中来实现
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == NSSelectorFromString(@"test:")) {
//void用v来表示,id参数用@来表示,SEL用:来表示
class_addMethod(self, sel, (IMP)test, "v@:@");
return YES;
}
return [super resolveInstanceMethod:sel];
}
在这里要看一下,千万别用错了方法
// 判断对象方法有没有实现
+(BOOL)resolveInstanceMethod:(SEL)sel
// 判断类方法有没有实现
+ (BOOL)resolveClassMethod:(SEL)sel
四、字典转模型
创建一个类DataItem
如下所示
@interface DataItem : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *tel;
@property (nonatomic, strong) NSArray *list;
+ (NSDictionary *)arrayPropertyGenericClass;
+ (DataItem *)toItem:(NSDictionary *)dict;
@end
#import "DataItem.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation DataItem
+ (NSDictionary *)arrayPropertyGenericClass
{
return @{
@"list":[DataItem class]
};
}
+ (id)toItem:(NSDictionary *)dict {
Class class = objc_msgSend(objc_msgSend([self class], @selector(alloc)), @selector(init));
NSDictionary *classDict = [DataItem arrayPropertyGenericClass];
//成员变量个数
unsigned int outCount = 0;
//获取DataItem所有成员变量的数组
Ivar *ivarList = class_copyIvarList(self, &outCount);
for (int i = 0; i < outCount; i++) {
//将成员变量从list中取出
Ivar oneIvar = ivarList[i];
//转换成NSString, 转换后的成员变量名字前面都带有 “_”
NSString *oneIvarStr = [NSString stringWithUTF8String:ivar_getName(oneIvar)];
//去掉 _
oneIvarStr = [oneIvarStr substringFromIndex:1];
//从字典中取出对应的value
id value = dict[oneIvarStr];
if ([classDict.allKeys containsObject:oneIvarStr]) {
//这个是array类型
Class subClass = classDict[oneIvarStr];
NSArray *valueArray = (NSArray *)value;
__block NSMutableArray *newMutList = [NSMutableArray arrayWithCapacity:outCount];
[valueArray enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSDictionary *oneDict = (NSDictionary *)obj;
id newClass = [subClass toItem:oneDict];
[newMutList addObject:newClass];
}];
value = newMutList;
}
if (value != nil) {
[class setValue:value forKey:oneIvarStr];
}
}
return class;
}
@end
字段转模型的主要思路就是
- 将类的所有成员变量通过
class_copyIvarList
提取出来- 将这些变量转换成对应的
String对象
,- 在字典中找出所对应的
value
- 利用KVC将
value
存入对应的key
当中
如果是array
类型,就需要指定一下里面的元素所对应的类,然后重复上面的操作即可。
五、总结
古有学好数理化,走遍全天下!
今有学会Runtime,干啥都不怕!
上面例子的下载地址
有什么想要用到runtime实现的小功能,欢迎在评论留言。如果有时间,我会试着做一下。