002-runtime 实现方法交换

1、方法调用的一个流程

  • 如上面的例子,是如何调用eat方法的
    • 对象方法:存储在类对象的方法列表中
    • 类方法:存储在元类的方法列表中
  • 寻找过程
    • 通过isa指针去对应的类中查找这个 eat:方法
    • 首先去类中的方法编号区去查找是否有eat:方法对应的编号(每当定义的方法的时候,都会在类的“方法编号区”注册一个编号)
    • 如果有这个编号,然后根据eat:的编号去类的“方法列表”中查找(方法列表中存储的只是最终函数实现的地址)
    • 根据这个函数地址去 “方法区” 调用对应的函数
p调用方法过程.png

2、方法交换的应用场景

  • 需求:当是[UIImage imageNamed:@"图片名"]设置图片的时候,要知道这个图片是否有内容,并且要知道是哪个“图片名”不存在而造成的

    • 策略一:当在使用[UIImage imageNamed:@"图片名"]的时候去判断并且输出造成失败的“图片名”。缺点:只要用到[UIImage imageNamed:@"图片名"]的地方就需要判断,代码会很臃肿。而且如果是旧项目这样的策略就更不合适了
    • 策略二:自定义一个UIImage,在内部实现这些功能。缺点:每次加载一个图片的时候都需要使用自定义的方法,这样是可以实现的,但是当这是一个旧项目的时候,这个方法也就不是一个好的策略。
    • 策略三: 给UIImage添加分类,重写imageNamed:方法,但是这样会覆盖系统的方法(系统优先调用分类中的方法),这个策略也不够好
    • 策略四:给UIImage添加分类,在分类中实现一个有扩展功能的方法,当调用imageNamed:方法的时候使用runtime来交换这个由扩展功能的方法
  • 使用runtime实现方法交换需要注意:防止“死循环”

//
//  UIImage+image.m
//  002-runtime(方法交换)
//
//  Created by 紫荆秋雪 on 2017/2/24.
//  Copyright © 2017年 Revan. All rights reserved.
//

#import "UIImage+image.h"
#import <objc/runtime.h>

@implementation UIImage (image)
// 把类加载进内存的时候调用,只会调用一次
+ (void)load {
    // 获取imageNamed方法
    // 获取哪个类的方法
    Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
    // 获取需要交换的方法
    Method revan_imageNamedMethod = class_getClassMethod(self, @selector(revan_imageNamed:));
    // 使用runtime来交换方法
    method_exchangeImplementations(imageNamedMethod, revan_imageNamedMethod);
}

//会被多次调用
//+ (void)initialize {
//
//}

+(UIImage *)revan_imageNamed:(NSString *)name {

    UIImage *image = [UIImage imageNamed:name];
    /// 扩展功能
    if (image) {
        NSLog(@"图片加载成功");
    } else {
        NSLog(@"图片加载失败-%@", name);
    }
    return image;
}

@end

  • 调用方法
//
//  ViewController.m
//  002-runtime(方法交换)
//
//  Created by 紫荆秋雪 on 2017/2/24.
//  Copyright © 2017年 Revan. All rights reserved.
//

#import "ViewController.h"
#import "UIImage+image.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *img = [UIImage imageNamed:@"1.png"];
    NSLog(@"%@", img);
}

@end

  • 为什么会出现“死循环”?
    • 当没有进行“方法交换”的时候,方法调用的过程
方法调用过程.png
  • 当方法交换之后,方法调用的过程
runtime交换方法后调用方法过程.png
  • runtime方法交换造成“死循环”原因:

  • 当调用imageNamed:方法的时候,会在会在方法编号区寻找,如果有就会找到存储方法实现的入口,此时由于已经方法交换,所以此地址是指向方法区revan_imageNamed方法实现,所以就会进入分类中定义的有扩展功能的revan_imageNamed的方法中,会执行[UIImage imageNamed]方法,会再次进入UIImage的方法编号区查询是否存在,和上面的过程一样依然会进入revan_imageNamed方法的实现中,这样就造成了“死循环”

  • 正确的runtime交换方法

//
//  UIImage+image.m
//  002-runtime(方法交换)
//
//  Created by 紫荆秋雪 on 2017/2/24.
//  Copyright © 2017年 Revan. All rights reserved.
//

#import "UIImage+image.h"
#import <objc/runtime.h>

@implementation UIImage (image)
// 把类加载进内存的时候调用,只会调用一次
+ (void)load {
    // 获取imageNamed方法
    // 获取哪个类的方法
    Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
    // 获取需要交换的方法
    Method revan_imageNamedMethod = class_getClassMethod(self, @selector(revan_imageNamed:));
    // 使用runtime来交换方法
    method_exchangeImplementations(imageNamedMethod, revan_imageNamedMethod);
}

//会被多次调用
//+ (void)initialize {
//
//}

+(UIImage *)revan_imageNamed:(NSString *)name {

    UIImage *image = [UIImage revan_imageNamed:name];
    /// 扩展功能
    if (image) {
        NSLog(@"图片加载成功");
    } else {
        NSLog(@"图片加载失败-%@", name);
    }
    return image;
}

@end

  • 调用方法
//
//  ViewController.m
//  002-runtime(方法交换)
//
//  Created by 紫荆秋雪 on 2017/2/24.
//  Copyright © 2017年 Revan. All rights reserved.
//

#import "ViewController.h"
#import "UIImage+image.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    UIImage *img = [UIImage imageNamed:@"1.png"];
    NSLog(@"%@", img);
}

@end

  • 这次调用方法的过程
    • 当在使用系统的UIImage *img = [UIImage imageNamed:@"1.png"];方法的时候,会在UIImage的方法编号区查询是否存在imageNamed:方法,如果存在就会通过"方法列表区"中的地址找到“方法区”中的函数实现,也就是revan_imageNamed:方法,在这个方法实现中有调用了UIImage *image = [UIImage revan_imageNamed:name];所以会在UIImage的方法编号区查找是否存储revan_imageNamed:的编号,如果有再通过“方法列表”中地址,找到“方法区”中的函数实现也就是系统方法 imageNamed方法。
runtime交换方法后调用方法过程.png

3、小结

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

推荐阅读更多精彩内容

  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,721评论 7 64
  • 导读:11、12月注定是不太平的月份,好多小型互联网创业公司都突然崩塌,最近一个朋友跟我抱怨道,说终于感受到了互联...
    柳骏阅读 9,688评论 11 167
  • 1️⃣runtime介绍: runtime是一套比较底层的纯C语言API, 包含了很多底层的C语言API。在我们平...
    CoderZS阅读 1,065评论 0 7
  • 搬到新房子里,有一个大大的阳台还有一个大大的衣柜,我记得之前你问过我,最希望在帝都住到什么样的房子,我毫不犹...
    COCO倩阅读 775评论 0 0
  • 当我今早听完子木的《金钱管理》分享,心里无比愉悦和兴奋,就在前10分钟,还收到别人送来的金钱哦,还是现金。这是否寓...
    垚垚涵涵妈阅读 685评论 0 4