Runtime的应用

来自:http://www.imlifengfeng.com/blog/?p=397
runtime是运行时库(Runtime Library),也简称运行时。
它是一个主要是C和汇编写的库,对C进行了特殊的处理,将结构体视为对象,将函数视为方法,使得C有了面向对象的能力,从而才创造了Objective-C。
这点也可以看出,C是编译时语言,而OC是动态运行时语言,所以在编译阶段,尽管OC中的方法没有实现也不会报错,而C会报错。
在运行时,OC语言才进行方法的处理,比如讲[person eat];转换为objc_msgSend(person, @selector(eat));然后通过person的isa指针找到person对应的class,在class中先去cache中通过SEL方法选择器查找对应的method,若缓存中未找到,再去methodList查找,若还未找到,便去父类中查找,如果这个过程中找到了该方法,便会自动将该方法加入cache中,方便下一次的查找,并且,编译器开始执行找到的这个函数。
1、快速归档

  • (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
    unsigned int outCount;
    Ivar * ivars = class_copyIvarList([self class], &outCount);
    for (int i = 0; i < outCount; i ++) {
    Ivar ivar = ivars[i];
    NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
    [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
    }
    }
    return self;
    }

  • (void)encodeWithCoder:(NSCoder *)aCoder {
    unsigned int outCount;
    Ivar * ivars = class_copyIvarList([self class], &outCount);
    for (int i = 0; i < outCount; i ++) {
    Ivar ivar = ivars[i];
    NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
    [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
    }

2、Json到Model的转化

  • (instancetype)initWithDict:(NSDictionary *)dict {

    if (self = [self init]) {
    //(1)获取类的属性及属性对应的类型
    NSMutableArray * keys = [NSMutableArray array];
    NSMutableArray * attributes = [NSMutableArray array];
    /*
    * 例子
    * name = value3 attribute = T@"NSString",C,N,V_value3
    * name = value4 attribute = T^i,N,V_value4
    */
    unsigned int outCount;
    objc_property_t * properties = class_copyPropertyList([self class], &outCount);
    for (int i = 0; i < outCount; i ++) {
    objc_property_t property = properties[i];
    //通过property_getName函数获得属性的名字
    NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
    [keys addObject:propertyName];
    //通过property_getAttributes函数可以获得属性的名字和@encode编码
    NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
    [attributes addObject:propertyAttribute];
    }
    //立即释放properties指向的内存
    free(properties);

      //(2)根据类型给属性赋值
      for (NSString * key in keys) {
          if ([dict valueForKey:key] == nil) continue;
          [self setValue:[dict valueForKey:key] forKey:key];
      }
    

    }
    return self;
    }

3、Category添加属性并生成getter和setter方法

import <Foundation/Foundation.h>

@interface NSArray (MyCategory)
//不会生成添加属性的getter和setter方法,必须我们手动生成
@property (nonatomic, copy) NSString *blog;
@end

import "NSArray+MyCategory.h"

import <objc/runtime.h>

@implementation NSArray (MyCategory)

// 定义关联的key
static const char *key = "blog";

/**
blog的getter方法
*/

  • (NSString *)blog
    {
    // 根据关联的key,获取关联的值。
    return objc_getAssociatedObject(self, key);
    }

/**
blog的setter方法
*/

  • (void)setBlog:(NSString *)blog
    {
    // 第一个参数:给哪个对象添加关联
    // 第二个参数:关联的key,通过这个key获取
    // 第三个参数:关联的value
    // 第四个参数:关联的策略
    objc_setAssociatedObject(self, key, blog, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    @end

4、Method Swizzling使用

import "UIViewController+swizzling.h"

import <objc/runtime.h>

@implementation UIViewController (swizzling)

  • (void)load {
    // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
    Method fromMethod = class_getInstanceMethod([self class], @selector(viewDidLoad));
    Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingViewDidLoad));
    /**
    • 我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。
    • 而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。
    • 所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。
      */
      if (!class_addMethod([self class], @selector(swizzlingViewDidLoad), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
      method_exchangeImplementations(fromMethod, toMethod);
      }
      }

// 我们自己实现的方法,也就是和self的viewDidLoad方法进行交换的方法。

  • (void)swizzlingViewDidLoad {
    NSString *str = [NSString stringWithFormat:@"%@", self.class];
    // 我们在这里加一个判断,将系统的UIViewController的对象剔除掉
    if(![str containsString:@"UI"]){
    NSLog(@"统计打点 : %@", self.class);
    }
    [self swizzlingViewDidLoad];
    }
    @end

5、Method Swizzling类簇
__NSArrayI才是NSArray真正的类

import "NSArray+ MyArray.h"

import "objc/runtime.h"

@implementation NSArray MyArray)

  • (void)load {//是加载文件到内存执行的方法 跟类的生命周期没有关系
    Method fromMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(objectAtIndex:));
    Method toMethod = class_getInstanceMethod(objc_getClass("__NSArrayI"), @selector(my_objectAtIndex:));
    method_exchangeImplementations(fromMethod, toMethod);
    }
  • (id)my_objectAtIndex:(NSUInteger)index {
    if (self.count-1 < index) {
    // 这里做一下异常处理,不然都不知道出错了。
    @try {
    return [self my_objectAtIndex:index];
    }
    @catch (NSException *exception) {
    // 在崩溃后会打印崩溃信息,方便我们调试。
    NSLog(@"---------- %s Crash Because Method %s ----------\n", class_getName(self.class), func);
    NSLog(@"%@", [exception callStackSymbols]);
    return nil;
    }
    @finally {}
    } else {
    return [self my_objectAtIndex:index];
    }
    }
    @end

6、runtime添加类方法
[objc] view plain copy
void study(id reccevier, SEL sel) {
}
// 如果调用的方法没有实现,就会走这个方法

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,635评论 0 9
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,109评论 0 7
  • runtime的理解和应用 项目中经常会有一些的功能模块用到runtime,最近也在学习它.对于要不要阅读runt...
    MYS_iOS_8801阅读 436评论 0 1
  • 1.归档和解档 - (void)encodeWithCoder:(NSCoder*)aCoder{ unsigne...
    生活本无度阅读 176评论 0 0
  • 项羽与项伯密谋起义良久,待兵马草粮齐备,便率先向当地的乡绅土豪发起了进攻,抢占第一根据地。 这天,项羽跨高头大马,...
    莫亘离荒阅读 360评论 2 1