 Runtime简介以及常见的使用场景

Runtime简介以及常见的使用场景

   Runtime简称运行时,是一套比较底层的纯C语言的API,作为OC的核心,运行时是一种面向对象的编程语言的运行环境,其中最主要的是消息机制,Objective-C就是基于运行时的。

   所谓运行时,是指尽可能地把决定从编译期推迟到运行期,就是尽可能地做到动态.只是在运行的时候才会去确定对象的类型和方法的.因此利用Runtime机制可以在程序运行时动态地修改类和对象中的所有属性和方法。

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

Objective-C从三种不同的层级上与Runtime系统进行交互,分别是

①通过Objective-C源代码;

②通过Foundation框架的NSObject类定义的方法;

③通过对Runtime函数的直接调用(需要导入#import );

   大部分情况下只管写OC代码就行,因为OC底层默认实现Runtime,每一个OC的方法,底层必然有一个与之对应的Runtime方法。。

以下是Runtime的一些使用场景:

1.发送消息

方法调用的本质,就是让对象发送消息。objc_msgSend,只有对象才能发送消息

举个简单的例子:如下

//(1).调用对象方法:

//创建Dog对象

Dog *dog = [[Dog alloc] init];

//调用对象方法

[dog run];

//调用对象本质:让对象发送消息

objc_msgSend(dog, @selector(run));

//(2).调用类方法方式:

//有两种

//第一种通过类名调用

[Dog run];

//第二种通过类对象调用

[[Dog class] run];

//用类名调用类方法,底层会自动把类名转换成类对象调用

//调用类方法本质:让类对象发送消息

objc_msgSend([Dog class],@selector(run));

消息机制原理:对象根据方法编号SEL去映射表查找对应的方法实现

2.动态添加方法

开发使用场景:如果一个类方法非常多,加载类到内存的时候也比较耗费资源,需要给每个方法生成映射表,可以使用动态给某个类,添加方法解决。经典面试题:有没有使用performSelector,其实主要想问你有没有动态添加过方法。简单使用

@implementation ViewController

-(void)viewDidLoad {

[super viewDidLoad];

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

//默认person,没有实现eat方法,可以通过performSelector调用,但是会报错。

//动态添加方法就不会报错

[p performSelector:@selector(eat)];

}

@end

@implementation Person

//void(*)()

//默认方法都有两个隐式参数,

void eat(id self,SEL sel)

{

NSLog(@"%@%@",self,NSStringFromSelector(sel));

}

//当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.

//刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法

+(BOOL)resolveInstanceMethod:(SEL)sel

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

//动态添加eat方法

/**第一个参数:给哪个类添加方法,第二个参数:添加方法的方法编号,第三个参数:添加方法的函数实现(函数地址),第四个参数:函数的类型,(返回值+参数类型) v:void @:对象->self :表示SEL->_cmd*/

class_addMethod(self, @selector(eat),eat, "v@:");  }

return [super resolveInstanceMethod:sel];

}

@end

注意:

   + (BOOL)resolveInstanceMethod:(SEL)aSEL这个函数与forwardingTargetForSelector类似,都会在对象不能接受某个selector时触发,执行起来略有差别。前者的目的主要在于给用户一个机会来向该对象添加所需的selector,后者的目的在于允许用户将selector转发给另一个对象。另外触发时机也不完全一样,该函数是个类函数,在程序刚启动,界面尚未显示出时,就会被调用。

   在类不能处理某个selector的情况下,如果类重载了该函数,并使用class_addMethod添加了相应的selector,并返回YES,那么后面forwardingTargetForSelector就不会被调用,如果在该函数中没有添加相应的selector,那么不管返回什么,后面都会继续调用forwardingTargetForSelector,如果在forwardingTargetForSelector并未返回能接受该selector的对象,那么resolveInstanceMethod会再次被触发,这一次,如果仍然不添加selector,程序就会报异常

3.运行时关联对象提高效率,给分类添加属性。

使用的时候与懒加载的特点相似,从`关联对象`中获取对象属性,如果有,直接返回。

@implementation ViewController

-(void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loadingthe view, typically from a nib.

//给系统NSObject类动态添加属性name

NSObject *objc = [[NSObject alloc] init];

objc.name = @"旺财";

NSLog(@"%@",objc.name);

}

@end

//定义关联的key

staticconst char *key = "name";

@implementation NSObject (Property)

-(NSString *)name

{

//根据关联的key,获取关联的值。

NSString*name =objc_getAssociatedObject(self, key);

if(name! = nil) {

return name;

}}

-(void)setName:(NSString *)name

{//第一个参数:给哪个对象添加关联

//第二个参数:关联的key,通过这个key获取

//第三个参数:关联的value

//第四个参数:关联的策略

objc_setAssociatedObject(self, key, name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

@end

4.使用运行时字典转模型

大体思路:利用运行时,遍历模型中所有属性,根据模型的属性名,去字典中查找key,取出对应的值,给模型的属性赋值。步骤:提供一个NSObject分类,专门字典转模型,以后所有模型都可以通过这个分类转。(所有字典转模型框架的核心算法)

创建NSObject的分类Runtime:


在.h中的类方法如下

#import

@interface NSObject (Runtime)

///给定一个字典,创建self类对应的对象

///@param dict字典

///@return对象

+ (instancetype)hd_objWithDict:(NSDictionary*)dict;

///获取类的属性列表数组


///@return类的属性列表数组

+ (NSArray*)hd_objProperties;

@end

在.m中的类方法如下:

//所有字典转模型框架的核心算法

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

//实例化对象

id object = [[self alloc]init];

//使用字典,设置对象信息

1>获得self的属性列表

NSArray *proList = [self hd_objProperties];

2>遍历字典

[dictenumerateKeysAndObjectsUsingBlock:^(id_Nonnullkey,id_Nonnullobj,BOOL*_Nonnullstop) {

NSLog(@"key %@ --- value %@", key,obj);

//

3>判断key是否在proList中

if([proListcontainsObject:key]) {

//说明属性存在,可以使用`KVC`设置数值

[objectsetValue:objforKey:key];

}}];

return object;

}

constchar* kPropertiesListKey ="CZPropertiesListKey";

+ (NSArray*)hd_objProperties{

//从`关联对象`中获取对象属性,如果有,直接返回!

//获取关联对象-动态添加的属性

NSArray*ptyList =objc_getAssociatedObject(self,kPropertiesListKey);

if(ptyList != nil) {

return ptyList;

}

//调用运行时方法,取得类的属性列表

//Ivar成员变量

//Method方法

//Property属性

//Protocol协议

//所有属性的`数组`,C语言中,数组的名字,就是指向第一个元素的地址

//retain/create/copy需要release,最好option + click

unsigned int count =0;

objc_property_t *proList =class_copyPropertyList([self class], &count);

NSLog(@"属性的数量%d", count);

//创建数组

NSMutableArray *arrayM = [NSMutableArrayarray];

//遍历所有的属性

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

// 1.从数组中取得属性

//C语言的结构体指针,通常不需要`*`

objc_property_t  pty = proList[i];

// 2.从pty中获得属性的名称

const char *cName =property_getName(pty);

NSString *name = [NSStringstringWithCString:cNameencoding:NSUTF8StringEncoding];

//NSLog(@"%@",name);

// 3.属性名称添加到数组

[arrayM addObject:name];

}

//释放数组

free(proList);

// ---

2.对象的属性数组已经获取完毕,利用关联对象,动态添加属性

/**

参数

1.对象self [OC中class也是一个特殊的对象]

2.动态添加属性的key,获取值的时候使用

3.动态添加的属性值

4.对象的引用关系

*/

objc_setAssociatedObject(self,kPropertiesListKey, arrayM.copy,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

return arrayM.copy;

}

注意:必须保证,模型中的属性和字典中的key一一对应。如果不一致,就会调用[ setValue:forUndefinedKey:],报key找不到的错。

分析:模型中的属性和字典的key不一一对应,系统就会调用setValue:forUndefinedKey:报错。

解决:重写对象的setValue:forUndefinedKey:,把系统的方法覆盖,

就能继续使用KVC,字典转模型了。

-(void)setValue:(id)value forUndefinedKey:(NSString *)key

{

}

   通过运行时字典转模型的好处在于写在NSObject的分类中,和类的关联性不强对类解耦,以后再做字典转模型的时候只需要把这个分类往任何一个程序中一拖,程序中的对象就都具备了这个字典转模型的方法。

5.交叉方法(黑魔法)

   开发使用场景:系统自带的方法功能不够,给系统自带的方法扩展一些功能,并且保持原有的功能。方式一:继承系统的类,重写方法.方式二:使用runtime,交换方法.

   Runtime在AFN中的使用细节:在AFN的NSURLSessionMangerM方法里面第363行写了一个静态的内联函数,做了一个交叉方法,交叉的是af_resume和resume方法,这样的话,可以在发送网络之前发起一个通知,能接受到任何一个网络请求的事件的变化。

@implementation ViewController

-(void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loadingthe view, typically from a nib.

//需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。

//步骤一:先搞个分类,定义一个能加载图片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;

//步骤二:交换imageNamed和imageWithName的实现,就能调用imageWithName,间接调用imageWithName的实现。

UIImage *image = [UIImageimageNamed:@"123"];

}

@end

@implementation UIImage (Image)

//加载分类到内存的时候调用

+(void)load

{

//交换方法

//获取imageWithName方法地址

Method imageWithName =class_getClassMethod(self, @selector(imageWithName:));

//获取imageWithName方法地址

Method imageName =class_getClassMethod(self, @selector(imageNamed:));

//交换方法地址,相当于交换实现方式

method_exchangeImplementations(imageWithName,imageName);

}

//既能加载图片又能打印

+(instancetype)imageWithName:(NSString *)name

{//这里调用imageWithName,相当于调用imageName

UIImage *image = [self imageWithName:name];

if (image == nil) {

NSLog(@"加载空的图片");}

return image;

}

@end

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,539评论 33 466
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,697评论 7 64
  • 黑桃先生让我不要洗衣服了 黑桃先生自己烧水 黑桃先生问我要不要热水袋 黑桃先生说把空调打开,他去端盆水到房间 黑桃...
    大圣快来阅读 336评论 0 0
  • 灯光献给了城市 我的忧虑就种下了病情 黑夜盖足了被子 我却迷失了垂听的声音 黎明唤醒了黑暗 后背再一次冰凉 治愈,...
    零温度阅读 170评论 0 4