ios runtime重要性_ios中Runtime的介绍以及使用

ios黑魔法--runtime介绍:

在Xcode5以后 ,苹果不建议开发者使用底层。为了能够使用runtime,我们需要做下面两个配置:

1.在需要使用runtime的地方导入#import。

2.在工程的Build Setting中搜索msg ,将其修改为NO。

引入:

在开发中,用对象去调用方法:

如:我们定义了一个Person类

其中有两个方法:

-(void)eat;//对象方法

+(void)eat;//类方法

Person *p = [[Person alloc]init];

[p eat];//调用对象方法

//p这个对象调用eat方法实际上是发送了一个消息去找到eat这个方法,并执行了这个方法:

objc_msgSend(p,@Selector(eat));

//类调用类方法,实际上也是用类对象去调用方法

Class personName = [Person class];

[personName PerformSelector:@Selector(eat)];

//实际上也执行了这个方法:

objc_msgSend(personName,@Selector(eat));

//PerformSelector这个方法就是一种动态添加方法,动态添加方法是一种懒加载的机制。

利用runtime扩展系统功能

如:现在有这样一个需求,imageName加载图片,但是我们并不能知道图片加载成功与否,我们想添加一个方法,知道image是否添加成功,所以我们就需要使用runtime来改造。

1.新建一个UIImage的category

2.导入#import

3.提供一个自己的方法:

+(_kindof UIImage*)hjt_imageNamed:(NSString *)imageName

{

//加载图片

UIImage *image = [UIImage imageNamed:imageName];

//进行图片是否为空的判断

if(image==nil){

NSLog(@"加载的图片为空,请注意");

}

return image;

}

4.重写load方法

+(void)load{

//Class_getInstanceMethod:获取对象方法

//Class_getMethodImplementation:获取类方法的实现

//Class_getClassMethod:获取类方法

Method imageNameMethod = Class_getClassMethod([UIImage class],@Selector(imageNamed:));

Method hjt_imageNameMethod = Class_getClassMethod([UIImage class],@Selector(hjt_imageNamed:));

//交换方法实现对系统方法的扩展

method_exchangeImplementations(imageNameMethod ,hjt_imageNameMethod);

//外部调用的时候还是调用imageNamed:

}

使用runtime动态添加方法:

对于动态添加方法实际上就是需要借助PerformSelector:这个方法来做事

如:我们为Person这个类动态添加一个eat的方法;

Person.m

//1.动态添加方法的第一步,先实现resolveInstanceMethod

//当我们在外面调用一个没有实现的方法时,就会调用resolveInstanceMethod

//备注:SEl其实只是一个方法的编号,系统会根据这个编号去找这个方法;

+(BOOL)resolveInstanceMethod:(SEL)sel{

if(sel==@Selector(eat)){

//"V@:"这个需要查官方runtime的文档,v表示eatMethod的返回值void

//@表示objc,:表示Selector

Class_addMethod(self,sel,(IMP)eatMethod,"v@:");

return YES;

}

return [super resolveInstanceMethod:sel];

}

// 定义eatMethod函数 注意是函数!

void eatMethod(id self,SEL _cmd){

}

//来到外部 我们初始化Person 然后调用eat这个方法

Person *p = [[Person alloc]init];

//这个是调用的不带参数的eat方法

[p performSelector:@Selector(eat)];

//如果要调用带参数的eat方法,我们需要进行下面几个地方的修改

//带参数:[p performSelector:@Selector(eat) withObject:@"apple"];

//1.修改Person.m中resolveInstanceMethod方法里的sel==@Selector(eat:);

//2.定义的函数eatMethod的时候,新增加一个参数 id param

//3.修改Class_addMethod方法中最后一个参数为"v@:@";

使用runtime动态添加属性

如:我们为NSObject添加一个userName这个属性

//动态添加属性就是一种动态的关联,让对象的某个属性去关联某块内存

1.新建一个NSObject的category

2.给某个对象产生关联,添加属性

//object:给那个对象添加属性 key:属性的名称(通过这个key拿到关联的对象) value:关联的值

-(void)setName:(NSString*)name{

objc_setAssociatedObject(self,@"userName",userName,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

-(NSString*)name{

return objc_getAssociatedObject(self,@"userName");

}

//这样我们就可以拿到NSObject对象中的userName这个属性了

使用runtime 进行字典转模型

1.写一个NSObject的category

在NSObject+model.m中:

+(instancetype)modelWithDict:(NSDictionary*)dict{

id obic = [[self alloc] init];

//class_copyIvarList:将成员属性列表复制一份传出去

//Ivar*:指向一个ivar数组的指针 count:成员属性的个数

unsigned int count = 0;

Ivar *ivarList = class_copyIvarList(self,&count);

for (int i = 0; i < count; i++){

//获取成员属性

Ivar ivar = ivarList [i];

//获取成员名

NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];

//获取数据类型

NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];

//获取key substringFromIndex:1 去除第0个_ 即将_count这种转换成count

NSString* key = [propertyName substringFromIndex:1];

id value = dict[key];

//对于解析出来还是一个字典,如 其中有一个字典 但是我们已经新建了一个User去解析这个字典,只有遇到这个User我们才进行这种二级转换

//二级转换

//值是字典,成员属性的类型不是字典,才需要转换成模型

//下面这个判断的意思就是遇到我们自定义的User*user进行转换 而NSDiction*dic不转换

if([value iskindofClass:[NSDictionary class]]&&![propertyType containString:@"NS"]){

NSRange range = [propertyType rangeOfString:@"\""];

propertyType = [propertyType substringFromIndex:range.location+range.length];

range = [propertyType rangeOfString:@"\""];

propertyType = [propertyType substringToIndex:range.location];

class modelClass = NSClassFromString(property);

if(modelClass){

value = [modelClass modelWithDict:value];

}

}

if(value){

[objc setValue:value forkey:key];

}

}

return objc;

}

//为UIbutton添加block 方便使用

//UIButton+HJTBlock.h

-(void)hjt_addEventHandler:(void (^) (UIButton *sender)) block forUIControlEvents:(UIControlEvents)controlEvents;

//UIButton+HJTBlock.m

#import "UIButton+HJTBlock.h"

#import

typedef void(^HJT_ButtonEventsBlock)(UIButton *sender);

@interface UIButton ()

//event callback

@property (nonatomic,copy) HJT_ButtonEventsBlock hjt_buttonEventBlock;

@end

@implementation UIButton (HJTBlock)

static void *hjt_buttonEventsBlockKey = &hjt_buttonEventsBlockKey;

-(HJT_ButtonEventsBlock)hjt_buttonEventBlock{

return objc_getAssociatedObject(self,&hjt_buttonEventsBlockKey);

}

-(void)setHjt_buttonEventBlock:(HJT_ButtonEventsBlock)hjt_buttonEventBlock{

objc_setAssociatedObject(self, &hjt_buttonEventsBlockKey,hjt_buttonEventBlock,OBJC_ASSOCIATION_COPY);

}

-(void)hjt_addEventHandler:(void (^)(UIButton *))block forUIControlEvents:(UIControlEvents)controlEvents{

self.hjt_buttonEventBlock = block;

[self addTarget:self action:@selector(hjt_buttonClick) forControlEvents:controlEvents];

}

-(void)hjt_buttonClick{

if (self.hjt_buttonEventBlock) {

self.hjt_buttonEventBlock(self);

}

}

@end

//使用:

UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 40, 40)];

btn.backgroundColor = [UIColor redColor];

[btn setTitle:@"hello" forState:UIControlStateNormal];

[self.view addSubview:btn];

[btn hjt_addEventHandler:^(UIButton *sender) {

NSLog(@"%@",sender.titleLabel.text);

sender.backgroundColor = [UIColor blueColor];

} forUIControlEvents:UIControlEventTouchUpInside];

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值