ios中Runtime的介绍以及使用

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 <objc/runtime.h>

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];
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容

  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,695评论 7 64
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 下面是常见的几种循环语句的写法,对其性能的影响也做了标识,希望会帮到你<br /> 1.慢,因为每次循环都会计算数...
    小啊美阅读 357评论 2 4
  • 今天是二月二,龙抬头的日子,我们单位开职代会。领导很鼓舞人心。祝福我们的未来越来越好。同学们乘机欢聚,年纪越大...
    海绵宝宝就是我阅读 239评论 0 0
  • 【放风】 第十一日,终于到了能和小无一起出门的日子,她在人生最忙碌的关键时刻,听到我卧榻的消息,便不顾一切风尘仆仆...
    1888弄阅读 216评论 0 0