iOS中OC的runtime使用场景
run time介绍:
由于OC是一门动态语言,所以他会把一些确定性的工作从编译链接时段推迟到运行时段。所以OC的运行被分成了编译和运行两个阶段,Runtime就是OC运行时的处理系统(一个用C语言的库),他是OC运行的基础;OC的运行时机制最主要是消息机制,对于C语言而言编译期就决定了运行时要调用哪个函数,而OC是动态进行的,在编译期只是确定要调用的函数指针名称,在运行时才会根据函数的指针去确定真正要调用的函数实现,所以我们才可以在运行时进行一些灵活的操作;
runtime.h中方法的定义:
- 对对象进行操作一般以objc_开头;
- 对类进行操作一般以class_开头;
- 对成员变量进行操作一般以ivar_开头;
- 对属性进行操作一般以property_开头;
- 对协议进行操作一般以protoco_开头;
以objc_开头的方法是最底层的操作方法,可以获取内存中的加载信息包括类,成员变量,属性,方法等列表信息。
主要的应用场景:
- 获取内存中所有的类名:
/*
打印出所有内存中的类名(在load方法中调用)
*/
+(void)logClasses{
unsigned int count = 0;
Class * classes = objc_copyClassList(&count);
for (int i = 0; i < count; i ++) {
const char * className = class_getName(classes[i]);
NSLog(@"className: %s",className);
}
}
- 发送消息的实质(消息机制):
+(void)sendFuncMsg{
webview * web = [[webview alloc]init];
// [web loadRequest];//相当于发送以下信息
//id :消息接受对象;SEL:要发送的消息; int:请求码;
int(*send)(id, SEL, int) = (int(*)(id, SEL, int)) objc_msgSend;//64位下需要如此转换
send(web, @selector(loadRequest),0);
}
- MethodSwizer(方法交换),黑魔法逆向开发中用的最多的方法:
+(void)sendFuncMsg{
}
+(void)changeMethoud{
//获取要交换的方法:
//self:要获取方法的类。
//@selector():要获取的方法
Method sendM = class_getClassMethod(self, @selector(sendFuncMsg));
Method newM = class_getClassMethod(self, @selector(newMethoud));
//交换方法的实现
method_exchangeImplementations(sendM, newM);
[self sendFuncMsg];
}
+(void)newMethoud {
NSLog(@"********啊哈哈,恭喜你方法交换成功了!!!!");
}
- 使用对象的动态绑定在分类中为类动态添加属性:
//category.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIView (UIView_cate)
@property(nonatomic,copy)NSString * lm;//要添加的属性
@end
NS_ASSUME_NONNULL_END
//category.m
#import "UIView+UIView_cate.h"
#import <objc/runtime.h>
//属性绑定的key
static const char * k_name = "name";
@implementation UIView (UIView_cate)
//category中添加的属性不会自动生成set、get方法以及带"_"的成员变量,所以需要手动添加set、get方法。(原因在上篇文category中有介绍)
-(NSString*)lm{
return objc_getAssociatedObject(self, k_name);
}
-(void)setLm:(NSString*)lm{
objc_setAssociatedObject(self, k_name, lm, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- 动态给类添加方法(可以使用category为类添加方法):
//添加的OC方法
-(void)addParametter:(NSString* )value{
NSLog(@"大家好,我是新添加的方法 %@",value);
}
//方法的默认隐式参数(添加的c函数)
void addCMethod(id self, SEL sel, NSString*value){
NSLog(@"大家好,我是新添加的方法 %@",value);
}
//当对象未实现某个方法时就会调用此方法 (为UIView添加方法);
//最后一个参数“ v@: ” 代表方法的类型,其中“v”代表返回值的类型为“void”,“@”表示对象为self, “ :”表示SEL为“ _cmd”, “@”代表要传入的参数
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(addParametter:)) {
NSLog(@"大家好 %@",NSStringFromSelector(sel));
//添加oc方法
class_addMethod(self, sel, class_getMethodImplementation(self, @selector(addParametter:)), "v@:@");
}
if (sel == @selector(addCMethod:)) {
//添加c函数
class_addMethod(self, sel, (IMP)addCMethod, "v@:@");
}
return [super resolveInstanceMethod:sel];
}
//在viewController中调用如下
[view performSelector:@selector(addParametter) withObject:@"人间四月芳菲尽,山寺桃花始盛开!"];
- 使用runtime进行字典转模型:
+(instancetype)getModelWith:(NSDictionary*)dic{
id objc = [[self alloc]init];
//使用runtime根据模型中的属性从字典中取出对应的值赋给模型
unsigned int count = 0;
Ivar * ivarList = class_copyIvarList(self, &count);
for (int i = 0; i < count; i ++) {
Ivar ivar = ivarList[i];//获取成员变量
NSString * ivtype = [NSString stringWithCString:ivar_getTypeEncoding(ivar) encoding:NSUTF8StringEncoding];//获取成员变量类型
NSString * ivname = [NSString stringWithCString:ivar_getName(ivar) encoding:NSUTF8StringEncoding];
NSString * key = [ivname substringFromIndex:1];// 去掉下划线
id value = [dic objectForKey:key];
NSLog(@"ivName: %@ ivType: %@ %@", ivname, key, value);//若有二级字典可以进一步转换
if ([value isKindOfClass:NSDictionary.class] && ![ivtype hasPrefix:@"NS"]) {
Class mClass = NSClassFromString(ivtype);
value = [mClass getModelWith:value];
}
if (value) {
//此三者有何去区别????(将在下篇kvc中介绍)
// [objc setObject:value forKey:key];
// [objc setValue:value forKey:key];
[objc setValue:value forKeyPath:key];
}
}
return objc;
}
runtime还有很多神奇的用法,此处只是列举了一部分,希望大家一起探索交流。