Objective-C之我所理解的Runtime

前言

Runtime,俗称运行时,是iOS非常核心的东西。我们都知道OC是一门动态的语言,它的动态其实就体现在运行时而不是编译时,通俗的说,在程序没有完全运行起来时,一切都有可能发生。正是因为这种机制,为我们提供了很多黑魔法,我们可以利用它做很多事情。由于runtime是基于C层面的一套API,所以学习它我们能够清楚很多OC层面代码的本质。本文不谈理论,不谈概念,只谈runtime在工作中的常用情景,毕竟理论的东西只有付诸于实践才能发挥价值。


1.为系统的类添加属性

  • 本质:就是让某个属性与对象产生关联.
  • 比如为NSObject添加一个name属性
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (Custom)

@property NSString *name;

@end

NS_ASSUME_NONNULL_END
#import "NSObject+Custom.h"
#import <objc/message.h>

@implementation MSObject (Custom)

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

- (NSString *)name {
    // 根据关联的key,获取关联的值。
    return objc_getAssociatedObject(self, @"name");
}
@end


2.动态添加方法

  • 因为OC是懒加载机制,只要实现了一个方法,就会被添加到方法列表中,占用内存。
  • 有一些APP,比如免费版和付费版,如果用户一直使用免费版,就没有必要把付费版才有的方法添加到方法列表;
  • 当用到这些方法的时候通过runtime为其动态添加
  • 从而减轻内存的压力.
// 方法调用


Person *person = [Person new];
// 无参数
[person performSelector:@selector(eat)];
// 1个参数
[person performSelector:@selector(drink:) withObject:@"cola"];
// 2个参数
[person performSelector:@selector(sleep:) withObject:@"Marry" withObject:@10];
    
#import "Person.h"
#import <objc/message.h>

void eat(id self, SEL _cmd) {
    NSLog(@"eat what tonight");
}

void drink(id self, SEL _cmd, NSString *name) {
    NSLog(@"I like dring %@", name);
}

void sleeps(id self, SEL _cmd, NSString *name, NSNumber *hours) {
    NSLog(@"Sleep with %@ for %@ hours", name, hours);
}

@implementation Person

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == NSSelectorFromString(@"eat")) {
        class_addMethod(self, sel, (IMP)eat, "v@:");
        return YES;
    } else if (sel == NSSelectorFromString(@"drink:")) {
        class_addMethod(self, sel, (IMP)drink, "v@:@");
    } else if (sel == NSSelectorFromString(@"sleep:")) {
        class_addMethod(self, sel, (IMP)sleeps, "v@:@");
    }
    return [super resolveInstanceMethod:sel];
}

@end


3.方法互换

  • 本质是将方法的实现进行了交换
image
#import "UIImage+Custom.h"
#import <objc/message.h>

@implementation UIImage (Custom)

// 把类加载进内存时调用,只调用一次
+ (void)load {
    Method imageNamedMethod = class_getClassMethod(self, sel_registerName("imageNamed:"));
    Method my_imageNamed = class_getClassMethod(self, @selector(my_imageNamed:));
    method_exchangeImplementations(imageNamedMethod, my_imageNamed);
}

+ (UIImage *)my_imageNamed:(NSString *)name {
    UIImage *image = [UIImage my_imageNamed:name];
    if (image) {
        NSLog(@"图片赋值成功,图片名称为:%@", name);
    } else {
        NSLog(@"图片赋值失败,找不到图片名称:%@", name);
    }
    return image;
}

@end

4.获取成员变量内部信息

  • 通过runtime可以查看一些没有开源的三方框架内部有哪些成员变量
 unsigned int count = 0;
    Ivar *varList = class_copyIvarList(self.class, &count);
    for (int i = 0; i < count; i++) {
        // 获取成员变量的名称
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(varList[i])];
        // 获取成员变量的类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(varList[i])];
    }

5.获取私有方法列表

  • 在代码调试的时候能够用的上.
unsigned int count = 0;
Method *methodList = class_copyMethodList(UIViewController.class, &count);
for (int i = 0; i < count; i++) {
    // 获取成员变量的名称
    SEL sel = method_getName(methodList[I]);
    NSString *selName = NSStringFromSelector(sel);
    NSLog(@"%@", selName);
}

6.消息处理

  • 都知道OC是消息机制,调用方法底层的实现都是发送消息;
    • [receiver message];
    • objc_msgSend(receiver, selector)
  • 当在相应的类以及父类中找不到类方法实现时会执行+resolveInstanceMethod:这个类方法;
  • 该方法如果在类中不被重写的话,默认返回NO。如果返回NO就表明不做任何处理,走下一步。如果返回YES的话,就说明在该方法中对这个找不到实现的方法进行了处理;
  • 在该方法中,我们可以为找不到实现的SEL动态的添加一个方法实现,添加完毕后,就会执行我们添加的方法实现;
  • 这样,当一个类调用不存在的方法时,就不会崩溃了。

7.APP防crash处理


最后

温故而知新。

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

推荐阅读更多精彩内容

  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 730评论 0 2
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,096评论 1 32
  • Objective-C 是基于 C 语言加入了面向对象特性和消息转发机制的动态语言。 面向对象和消息转发是 Obj...
    吴佩在天涯阅读 1,129评论 2 6
  • 文中的实验代码我放在了这个项目中。 以下内容是我通过整理[这篇博客] (http://yulingtianxia....
    茗涙阅读 920评论 0 6
  • 何必因为别人的渴望而去迎合 何必因为经历的痛苦而延缓前行 我就是我 经历婚姻 经历生儿育女 经历生离死别 经历失败...
    瓢和碗阅读 173评论 0 0