iOS篇-RunTime篇-在实战项目中的应用

TZ : 假如孤独的时候会,我们应该庆幸至少自己还是在这个地球上,没有被遗落在黑暗的宇宙里

一 : 科普一分钟

runtime大家总能听到,或者在框架中看到,但是在开发项目的时候,似乎没有用到过,读代码的时候也是匆匆略过,但是它的好处确实很多,能帮助我们解决一些曾经绞尽脑汁,但却无功而返的问题,和一些项目需求上的复杂问题,下面一一介绍在实战中的开发技巧.

二 : 项目开发中的实战应用

1. 简介

RunTime简称运行时,OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的就是消息机制.

相对于C语言函数的调用,在编译的时候会决定调用哪个函数,而对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用.

事实证明,在编译阶段:OC可以调用任何函数,即使这个函数并未实现,只要声明就不会报错.

而对于C语言,在编译阶段,调用未实现的函数就会报错.

2.消息发送

任何方法调用本质:发送一个消息,用runtime发送消息,OC底层实现通过runtime实现

示例代码:一个对象如果创建,开辟空间的

     //xcode6苹果不推荐使用runtime
    
    //找到build setting -> 搜索msg NO

    //id:谁发送消息
    //SEL : 发送什么消息

 //    id objc = [NSObject alloc];
    id objc = objc_msgSend([NSObject class], @selector(alloc));
    
    //    objc = [objc init];
    
    objc = objc_msgSend(objc, @selector(init));

最终生成消息机制,编译器做的事情,最终代码,需要把当前代码重新编译,用xcode编译器 ,最终生成代码-转换成c++代码

3.对象发送消息

首先创建一个对象,里面有几个我们实现的方法

@interface TZperson : NSObject

-(void)eat;

-(void)TZeat:(NSString*)food;

实现消息发送

 TZperson *p = objc_msgSend(objc_getClass("TZperson"),sel_registerName("alloc"));
    
    
    //    p = [p init];
    p = objc_msgSend(p, sel_registerName("init"));
    
    
    //调用
    //    [p TZeat:@"一块巧克力"];
    objc_msgSend(p, @selector(TZeat:),@"一块巧克力");

注意 objc_getClass("TZperson)"[TZperson Class] 同意

过程分析 : 如何调用 TZeat:方法的
1.通过isa去对应的类中查找,
2.注册方法编号(把方法名转换成方法编号)
3.根据方法编号去查找对应的方法
4.找到的只是最终函数实现的地址,根据地址去方法区调用对应的函数

图解分析 :

图解1
4.Runtime交换方法 : 只想修改系统的方法实现

情景 : 当有一项目的一个系统方法 我们以 [UIImage imageNamed:@"1.jpeg"]; 为例,为这个方法添加一个功能,判断图片是否读取成功,假如这个项目有200个地方使用了系统的这个方法,我们有什么好的办法,来解决上述需求吗?

有人会想到自定义方法.这个方法倒是可以,但是这么做未免工作量太大了.所以我们想到用runtime交换方法来实现上述需求

代码示例 :

外部 : 我们要给下面这个 系统方法添加功能

    UIImage *image = [UIImage imageNamed:@"1.jpeg"];

内部 : 所以要写一个分类,来完成方法交换

@interface UIImage (image)
+(UIImage*)TZ_imageNamed:(NSString *)name;

@end
//把类加载进内存的时候调用,只会调用一次
+(void)load{
    
    
    
//交换方法 runtime 交换方法
    //获取imageNamed
    //获取哪个方法
    //SEL:获取哪个方法
    Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
    
    
    //获取TZ_imageNamed
    Method TZimageNamedMethod = class_getClassMethod(self, @selector(TZ_imageNamed:));
    
   //交换方法: runtime
    method_exchangeImplementations(imageNamedMethod, TZimageNamedMethod);
    
    //调用imageNamed

}
+(UIImage*)TZ_imageNamed:(NSString *)name{

 UIImage *image = [UIImage TZ_imageNamed:name];
    if (image) {
        NSLog(@"加载成功");
    }else{
        NSLog(@"加载失败");
    }
    return image;
}

原理 : 与对象发送消息相似,只不过在指向方法区的时候 交换了两个函数的方法实现.

5. Runtime添加方法

需求分析 : 某个对象没有实现某个方法,但是我们却想用如何实现

外部 :

-(void)TZaddMethod{
    
  
    
    TZperson *person = [[TZperson alloc]init];
    
    //执行为实现方法    
    [person performSelector:@selector(TZplay:) withObject:@"人鱼表演"];
    
    
    
}

内部

//任何方法默认都有两个隐式参数,self,_cmd
//什么时候调用:只要一个对象调用了一个未实现的方法就会调用这个方法,进行处理
//作用 : 动态添加方法,处理未实现
+(BOOL)resolveInstanceMethod:(SEL)sel{

    
    
    if (sel == NSSelectorFromString(@"TZplay:")) {
        
        //TZdrink
        //Class : 给哪个类添加方法
        //SEL : 添加哪个方法
        //IMP : 方法实现 ==>函数 ==>函数入口==>函数名
        //type : 方法类型
        class_addMethod(self, sel, (IMP)tzaaa, "v@:@");
        return YES;
        
    }
    
        return [super resolveInstanceMethod:sel];


}
void tzaaa(id self,SEL _cmd,NSString *play){
    NSLog(@"观赏了%@",play);

}

官方文档 :

官方文档
6.RunTime动态添加属性

需求分析 : 当我们想给系统扩充一个属性的时候,大家首先做的 是 建立类别,但是类别中的 属性 是没有setget的 如何实现呢.用runtime来实现看看.

示例代码 :

// @property分类:只会生成get ,set方法生明,不会生成实现,也不会生成下划线成员属性
@property NSString *name;
-(void)setName:(NSString *)name{

//    _name = name;

    
    // object:给哪个对象添加属性
    //key : 属性名称
    //value : 属性值
    //policy : 保存策略
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    


}


-(NSString *)name{

//    return _name;
    
    return objc_getAssociatedObject(self, @"name");
    
}

原理分析 : 动态添加属性,就是让某个属性与对象产生关联,一般都是针对系统的类

7.runtime字典转模型

需求分析 : 自动根据模型来解析字典,对模型和子模型进行赋值

外部

 TZStatesItem *item = [TZStatesItem modelWithDic:dict];

内部

@interface NSObject (Model)
//字典转模型
+(instancetype)modelWithDic:(NSDictionary*)dic;


@end
//本质:创建谁的对象
+(instancetype)modelWithDic:(NSDictionary*)dic{

    id objc = [[self alloc]init];
    
    //Ivar:成员变量  以下划线开头
    //property:属性
    
    //runtime : 根据模型属性,去字典中取出对应的value给模型属性赋值
    //1.获取模型中所有成员变量 key
    // 获取哪个类的成员变量
    //count : 成员变量个数
    
  unsigned  int count = 0;
    //获取成员变量数组
   Ivar *ivarList = class_copyIvarList(self, &count);
   
    //遍历所有成员变量名字
    for (int i = 0; i < count; i++) {
        //获取成员变量
        Ivar ivar = ivarList[i];
        //获取成员变量名字
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        //            @\"user\" -> user
        
        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
        
        //获取key
        NSString *key = [ivarName substringFromIndex:1];
        
        //去字典中查找对应的value
        id value = dic[key];
        
        
        //二级转换 : 判断下value 是否是字典,如果是,字典转换成对应的模型,并且是自定义对象才转换
        if ([value isKindOfClass:[NSDictionary class]] && ![ivarType hasPrefix:@"NS"]) {
          
   
        //获取类
            Class modelClass = NSClassFromString(ivarType);
            value = [modelClass modelWithDic:value];
       
        
        }
    
        //给模型中属性赋值
        if (value) {
            [objc setValue:value forKey:key];
            
        }
        
    }

    //2.根据属性名去字典中查找value
    //3.给模型中属性赋值 KVC
    return objc;
    
}

延展 : 上述获取属性列表和成员列表功能也可以用于,归档和反归档的需求中,减少了大量冗余代码.

三 : 总结

总体来说,runtime在我们的实际开发中运用的不多,尽量不要为了运用而运用,在使用中,解决一些棘手和难处理的问题.活学活用.

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

推荐阅读更多精彩内容