如何对类方法进行 Method Swizzling

Method Swizzling 应该是很多开发者都非常熟悉并且经常接触的技术,也是 Objective-C Runtime 的一大特色,但是很多人在使用的时候也许只做过对实例方法进行 swizzling,但是对于类方法也就是加号方法,常用的手段却不能实现。

我曾经搜索过有关如何对类方法进行 Method Swizzling 的方法,但是相关的问题非常少,没有直接解决,索性从 Runtime 源码开始入手,研究这个问题。

在开始之前,先放出本次的 Demo 代码:

static void SwizzleMethod(Class cls, SEL ori, SEL rep) {
    Method oriMethod = class_getInstanceMethod(cls, ori);
    Method repMethod = class_getInstanceMethod(cls, rep);
    
    BOOL flag = class_addMethod(cls, ori, method_getImplementation(repMethod), method_getTypeEncoding(repMethod));
    
    if (flag) {
        class_replaceMethod(cls, rep, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
    } else {
        method_exchangeImplementations(oriMethod, repMethod);
    }
}

@interface Foo : NSObject

+ (void)bar;

@end

@implementation Foo

+ (void)bar {
    NSLog(@"[Foo bar] called!!");
}

@end


@implementation Foo (Swizzle)

+ (void)swz_bar {
    NSLog(@"Before calling ----");
    [self swz_bar];
    NSLog(@"After calling ----");
}

@end

最终要实现对 Foo 的类方法 bar 进行重新调配,我的切入点放到了 class_getInstanceMethod 这个 Runtime 方法上,这个函数大体做了以下的工作:检查参数,从缓存里查找,如果没找到再从方法列表中查找。其实,Runtime 还提供了一个 class_getClassMethod 函数,用来获取类方法,它的源码如下:

Method class_getClassMethod(Class cls, SEL sel)
{
    if (!cls  ||  !sel) return nil;

    return class_getInstanceMethod(cls->getMeta(), sel);
}

可以看到,它实际上还是调用了 class_getInstanceMethod,只不过 cls 参数变成了 cls->getMeta(),继续跟踪:

Class getMeta() {
    if (isMetaClass()) return (Class)this;
    else return this->ISA();
}

最终它返回了一个 Class 对象的 isa 指针,你可能会问这是什么意思呢?是时候祭出这张图了:

Objective-C Class Diagram

Objective-C 的类型系统设计也是十分巧妙,我们可以通过 object_getClass 这个方法获取到一个实例的类对象,其实它走的就是 isa 指针,假设我们已经得到 Foo 的类对象了(如图中间一列所示),我们再次使用 object_getClass 就可以得到最右列的 Meta-Class 了,所以 Meta-Class 又是什么呢?

Objective-C 中使用 id 类型来表示一个实例对象,id 实质就是 objc_object *typedef,一个实例对象的结构体里存储了自己 ivar 的值和一些其他的信息,而它的实例方法都存在于中间那一列 objc_class 对象中。当我们使用 [foo bar] 时,Runtime 会通过 foo->isa 找到 objc_class,然后在里面找到相应方法调用。但如果我们使用 [Foo bar],此时的 receiver 是一个 objc_class,Runtime 同样会顺着 isa 指针找,最终找到了 Meta-Class,自然地,类方法就在那里面。

所以,回到咱们的问题上,如何对类方法进行 Method Swizzling,自然就是对一个类的 Meta-Class 进行操作即可。我们看一下操作实例方法的代码:

SwizzleMethod([Foo class], @selector(bar), @selector(swz_bar));

那么要操作类方法,就是把 cls 参数变成这个类的 Meta-Class 即可:

SwizzleMethod(object_getClass([Foo class]), @selector(bar), @selector(swz_bar));

就是这么简单!看下效果:


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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,633评论 0 9
  • Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的...
    有一种再见叫青春阅读 551评论 0 3
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,107评论 0 7
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,468评论 33 467
  • 原文出处:南峰子的技术博客 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了...
    _烩面_阅读 1,192评论 1 5