Runtime(运行时)总结

基本解释

Runtime 是一套比较底层的纯C语言API 它是OC的幕后工作者 我们平时写的OC代码        在运行时都会编译器转为runtime的C语言代码 其中最主要的是消息机制OC的函数调用成为消息发送 属于动态调用过程 在编译的时候并不能决定真正调用哪个函数事实证明在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要申明过就不会报错而C语言在编译阶段就会报错 只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

简单实例

obj doSometing其中obj是一个对象,makeText是一个函数名称。对于这样一个简单的调用。在编译时RunTime会将上述代码转化成:objc_msgSend(obj,@selector(doSomething);首先通过obj的isa指针找到obj对应的class。在Class中先去cache中 通过SEL查找对应函数method(猜测]cache中method列表是以]EL为key通过hash表来存储的,这样能提高函数查找速度),若 cache中未找到。再去methodList中查找,若methodlist中未找到,则取superClass中查找。若能找到,则将method加 入到cache中,以方便下次查找,并通过method中的函数指针跳转到对应的函数中 

实际应用

Json到Model的转化

在开发中相信最常用的就是接口数据需要转化成Model了(当然如果你是直接从Dict取值的话。。。),很多开发者也都使用著名的第三方库如JsonModel、Mantle或MJExtension等,如果只用而不      知其所以然,那真和“搬砖”没啥区别了,下面我们使用runtime去解析json来给Model赋值。原理描述:用runtime提供的函数遍历Model自身所有属性,如在json中有对应的值,则将其赋值。核心方法:在NSObject的分类中添加方法 1  - (instancetype)initWithDict:(NSDictionary *)dict {2    3      if (self = [self init]) {4          //(1)获取类的属性及属性对应的类型5          NSMutableArray * keys = [NSMutableArray array];6          NSMutableArray * attributes = [NSMutableArray array];7          /*8            * 例子9            * name = value3 attribute = T@"NSString",C,N,V_value310          * name = value4 attribute = T^i,N,V_value411          */12          unsigned int outCount;13          objc_property_t * properties = class_copyPropertyList([self class], &outCount);14          for (int i = 0; i < outCount; i ++) {15              objc_property_t property = properties[i];16              //通过property_getName函数获得属性的名字17              NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];18              [keys addObject:propertyName];19              //通过property_getAttributes函数可以获得属性的名字和@encode编码20              NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];21              [attributes addObject:propertyAttribute];22          }23          //立即释放properties指向的内存24          free(properties);25  26          //(2)根据类型给属性赋值27          for (NSString * key in keys) {28              if ([dict valueForKey:key] == nil) continue;29              [self setValue:[dict valueForKey:key] forKey:key];30          }31      }32      return self

快速归档

有时候我们要对一些信息进行归档,如用户信息类UserInfo,这将需要重写initWithCoder和encodeWithCoder方法,并对每个属性进行encode和decode操作。那么问题来了:当属性只有几个的时候可以轻松写完,如果有几十个属性呢?那不得写到天荒地老?。。。原理描述:用runtime提供的函数遍历Model自身所有属性,并对性encode和decode操作核心方法:在Model的基类中重写方法:  1  - (id)initWithCoder:(NSCoder *)aDecoder {2      if (self = [super init]) {3          unsigned int outCount;4          Ivar * ivars = class_copyIvarList([self class], &outCount);5          for (int i = 0; i < outCount; i ++) {6              Ivar ivar = ivars[i];7              NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];8              [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];9          }10      }11      return self;12  }13  14  - (void)encodeWithCoder:(NSCoder *)aCoder {15      unsigned int outCount;16      Ivar * ivars = class_copyIvarList([self class], &outCount);17      for (int i = 0; i < outCount; i ++) {18          Ivar ivar = ivars[i];19          NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];20          [aCoder encodeObject:[self valueForKey:key] forKey:key];21      }22  }

访问私有变量

我们知道,OC中没有真正意义上的私有变量和方法,要让成员变量私有,要放在m文件中声明,不对外暴露。如果我们知道这个成员变量的名称,可以通过runtime获取成员变量,再通过getIvar来获取它的值。方法:  1.Ivar ivar = class_getInstanceVariable([Model class], "_str1"); 2.NSString * str1 = object_getIvar(model, ivar)

给分类(Category)添加属性

遇到一个问题,写了一个分类,但原先类的属性不够用。添加一个属性,调用的时候崩溃了,说是找不到getter、setter方法。查了下文档发现,OC的分类允许给分类添加属性,但不会自动生成getter、setter方法。有没有解决方案呢?有,通过运行时建立关联引用。接下来以添加一个这样的属性为例:@property (nonatomic, copy) NSString *str;在匿名分类或者头文件中添加属性。区别是:匿名分类中添加的是私有属性,只在本类中可以使用,类的实例中不可以使用。头文件中添加的在类的实例中也可以使用。 //分类的头文件@interface ClassName (CategoryName)//我要添加一个实例也可以访问的变量所以就写在这里了@property (nonatomic, strong) NSString *str;@end//匿名分类@interface ClassName ()@end3、在实现里面写要添加属性的getter、setter方法。@implementation ClassName (CategoryName) -(void)setStr:(NSString *)str  {      objc_setAssociatedObject(self, &strKey, str, OBJC_ASSOCIATION_COPY);  }  -(NSString *)str  {      return objc_getAssociatedObject(self, &strKey);  }@end在setStr:方法中使用了一个objc_setAssociatedObject的方法,这个方法有四个参数,分别是:源对象,关联时的用来标记是哪一个属性的key(因为你可能要添加很多属性),关联的对象和一个关联策略。 用来标记是哪一个属性的key常见有三种写法,但代码效果是一样的,如下: //利用静态变量地址唯一不变的特性1、static void *strKey = &strKey;2、static NSString *strKey = @"strKey"; 3、static char strKey;​关联策略是个枚举值,解释如下:enum {    OBJC_ASSOCIATION_ASSIGN = 0, //关联对象的属性是弱引用 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //关联对象的属性是强引用并且关联对象不使用原子性OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //关联对象的属性是copy并且关联对象不使用原子性OBJC_ASSOCIATION_RETAIN = 01401, //关联对象的属性是copy并且关联对象使用原子性OBJC_ASSOCIATION_COPY = 01403 //关联对象的属性是copy并且关联对象使用原子性};​4、完成后的整体代码如下:.h文件//分类的头文件@interface ClassName (CategoryName)@property (nonatomic, strong) NSString *str;@end.m文件//实现文件static void *strKey = &strKey;@implementation ClassName (CategoryName) -(void)setStr:(NSString *)str  {      objc_setAssociatedObject(self, & strKey, str, OBJC_ASSOCIATION_COPY);  }  -(NSString *)str  {      return objc_getAssociatedObject(self, &strKey);  }@end

Method swizzling(方法交换“黑魔法”)

方法交换,顾名思义,就是将两个方法的实现交换。例如,将A方法和B方法交换,调用A方法的时候,就会执行B方法中的代码,反之亦然。 话不多说,这是参考Mattt大神在NSHipster上的文章自己写的代码。import "UIViewController+swizzling.h"import@implementation UIViewController (swizzling)

//load方法会在类第一次加载的时候被调用

//调用的时间比较靠前,适合在这个方法里做方法交换

+ (void)load{

//方法交换应该被保证,在程序中只会执行一次

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

//获得viewCo\ntroller的生命周期方法的selector

SEL systemSel = @selector(viewWillAppear:);

//自己实现的将要被交换的方法的selector

SEL swizzSel = @selector(swiz_viewWillAppear:);

//两个方法的Method

Method systemMethod = class_getInstanceMethod([self class], systemSel);

Method swizzMethod = class_getInstanceMethod([self class], swizzSel);

//首先动态添加方法,实现是被交换的方法,返回值表示添加成功还是失败

BOOL isAdd = class_addMethod(self, systemSel,

method_getImplementation(swizzMethod),

method_getTypeEncoding(swizzMethod));

if (isAdd) {

//如果成功,说明类中不存在这个方法的实现

//将被交换方法的实现替换到这个并不存在的实现

class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));

}else{

//否则,交换两个方法的实现

method_exchangeImplementations(systemMethod, swizzMethod);

}

});

}

- (void)swiz_viewWillAppear:(BOOL)animated{

//这时候调用自己,看起来像是死循环

//但是其实自己的实现已经被替换了

[self swiz_viewWillAppear:animated];

NSLog(@"swizzle");

}

@end

在一个自己定义的viewController中重写viewWillAppear

- (void)viewWillAppear:(BOOL)animated{

[super viewWillAppear:animated];

NSLog(@"viewWillAppear");

}

//Run起来看看输出吧!

我的理解:

Swizzling应该写在+load方法中,因为+load是在类被初始化时候就被调用的。+initialize是在收到消息之后才调用,如果应用不发送消息给它,它就永远不可能执行。

Swizzling应该被写在dispatch_once中,保证只被执行一次和线程安全。

如果类中已经有了可替换的方法,那么就调用method_exchangeImplementations交换,否则调用class_addMethod和class_replaceMethod来完成替换。

xxx_viewWillAppear:方法的看似会引发死循环,但其实不会。在Swizzling的过程中xxx_viewWillAppear:已经被重新指定到UIViewController类的-viewWillAppear:中。不过如果我们调用的是viewWillAppear:反而会产生无限循环,因为这个方法的实现在运行时已经被重新指定为xxx_viewWillAppear:了。

方法交换对于我来说更像是实现一种思想的最佳技术:AOP面向切面编程。

既然是切面,就一定不要忘记,交换完再调回自己。

一定要保证只交换一次,否则就会很乱。

最后,据说这个技术很危险,谨慎使用。

防止数组越界 使用交换方法 越界时动态使用方法 但是谨慎使用

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,670评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,159评论 0 7
  • Objective-C语言是一门动态语言,他将很多静态语言在编译和链接时期做的事情放到了运行时来处理。这种动态语言...
    tigger丨阅读 1,370评论 0 8
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 719评论 0 2
  • 本文转载自:http://yulingtianxia.com/blog/2014/11/05/objective-...
    ant_flex阅读 737评论 0 1