曾静的博客

但行好事,莫问前程.

嗨,我是曾静 (@devzeng),目前暂居深圳。


这是我用来记录平日学习笔记的地方,欢迎您的访问.

Objective-C中的Method Swizzling

Objective-C对象在收到消息之后会经过Dynamic Message Dispatch System(动态消息派发系统)来进行处理,该系统会查出消息对应的方法并执行其代码。那么对于给定@selector名称相对应的方法是否可以在运行期可以动态改变呢?如果能善用这个特性,则可发挥出巨大优势,因为我们可以不需要源码也不需要通过继承子类来覆写对应的方法就能改变这个类本身的功能。

没错,Objective-C中确实提供了这样的操作,这就是我们这里会介绍到的Method Swizzling(方法的调配)。在Objective-C中每个类都有一个方法列表,类的方法列表会把@selector映射到相关的方法实现之上,使得Dynamic Message Dispatch System(动态消息派发系统)能够根据这个找到应该调用的方法,这些方法均以函数指针(IMP)的形式来表示,其原型如下:

id (*IMP)(id, SEL, ...)

应用实例

在实际的应用中,使用Method Swizzling直接来交换两个方法的实现的意义不大,但是可以通过这一手段来为既有的方法实现添加新增的功能,比如说在调用lowercaseString方法适合记录某些信息,这个时候可以通过交换方法实现来达成这个目标,我们新增一个方法在此方法中实现所需的附加功能,并调用原有的实现。

NSString+Extension.h

@interface NSString (Extension)

- (NSString *)devzeng_lowercaseString;

@end

NSString+Extension.m

@implementation NSString (Extension)

- (NSString *)devzeng_lowercaseString {
    NSString *lowercase = [self devzeng_lowercaseString];
    NSLog(@"【Debug】%@->%@", self, lowercase);
    return lowercase;
}

@end

上面的代码看上去好像会陷入递归调用的死循环,其实不然。此方法是准备和lowercaseString互换的,所以在运行期devzeng_lowercaseString其实对应的是lowercaseString,最后通过下面的代码来实现互换。

main.m

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Method ori_Method =  class_getInstanceMethod([NSString class], @selector(lowercaseString));
        Method my_Method = class_getInstanceMethod([NSString class], @selector(devzeng_lowercaseString));
        method_exchangeImplementations(ori_Method, my_Method);
        
        NSString *string = @"Hello, World!";
        NSString *lowercaseString = [string lowercaseString];    
    }
    return 0;
}

输出结果:【Debug】Hello, World!->hello, world!

通过上面的示例可以看出,我们可以通过Method Swizzling为一些不透明的类的某些方法做“黑盒测试”,比如增加一些日志信息等,这样在一定的程度上有利于程序的调试。然而,此做法如果滥用会造成代码变得不易读而且不利于维护。

Method Swizzling的陷阱

使用Method Swizzling就好比在厨房用一把锋利的小刀。有些人担心小刀会割伤自己而害怕使用,然而有些人却钟情于使用它。虽然说Method Swizzling能够让我们写出更好、更高效、更加可维护的代码但是它也可能给我们带来很多很麻烦的bug。下面是一些在使用Method Swizzling会遇到的让人头疼的陷阱。

  • Method swizzling is not atomic
  • Changes behavior of un-owned code
  • Possible naming conflicts
  • Swizzling changes the method’s arguments
  • The order of swizzles matters
  • Difficult to understand (looks recursive)
  • Difficult to debug

关于上面的内容的细致描述请移步《Objective-C的hook方案(一): Method Swizzling》(中文版本)和 《Swizzle method》 (英文版本)

深度阅读

1、《Objective-C的hook方案(一): Method Swizzling》

2、《Swizzle method》

最近的文章

使用Ant实现iOS项目的自动编译打包

Apache Ant,是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,大多用于Java环境中的软件开发。由Apache软件基金会所提供维护,目前最新的版本是1.9.4。本文主要介绍如何在iOS开发中使用Ant来提高开发效率,重点涉及到ant的安装、build配置文件的基本语法和iOS build脚本等内容。安装ANT工具1、到Apache Ant的官网上下载最新的ant工具包可以直接使用浏览器下载,或者是其他下载工具。下载地址也可以使用如下的命令:wget http://...…

iOS继续阅读
更早的文章

Objective-C中的Associated Objects

Associated Objects(关联对象)或者叫做关联引用(Associated References),是作为Objective-C 2.0运行时功能被引入到Mac OS 10.6 Snow Leopard(及iOS4)系统。与它相关在<objc/rumtime.h>中有3个C函数,他们可以让对象在运行时关联任何值:(1)用给定的key和policy来为指定对象(object)设置关联对象值(value)void objc_setAssociatedObject(id ...…

iOS继续阅读