博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ios中Runtime的介绍以及使用
阅读量:5960 次
发布时间:2019-06-19

本文共 5711 字,大约阅读时间需要 19 分钟。

###ios黑魔法--runtime介绍:

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

1.在需要使用runtime的地方导入#import<objc/message.h>。 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<objc/message.h> 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];复制代码

转载于:https://juejin.im/post/5a30f7056fb9a04528467bc5

你可能感兴趣的文章
如何解决mysql主从延迟
查看>>
iOS App 之间的相互跳转
查看>>
iOS基于百度地图的开发 (百度地图BMKSearch问题) (作者不允许转载 我也没办法 ......
查看>>
往事两三则
查看>>
使用LiveData和DataBinding进行双向绑定
查看>>
Convert Url to InetAddress
查看>>
oracle 限制特定ip登录
查看>>
解酒方法
查看>>
vi 命令
查看>>
1.1
查看>>
Elasticsearch 安装与启动
查看>>
[logstash-input-redis]插件使用详解
查看>>
优化应用的电池寿命(笔记)-1
查看>>
SSH Secure Shell Client
查看>>
JFinal源码分析------初始化那些事儿
查看>>
处理 允许远程协助连接这台计算机 灰色
查看>>
使用Jquery 加载页面时调用JS
查看>>
css+div+jquery弹出层
查看>>
求职相关(链接,不定期更新)
查看>>
pdo 连接数据库 报错 could not find driver 解决方法
查看>>