初探Method Swizzling

Method Swizzling: 即方法交换。

先来学点Objective-C的运行时来热热身。

一、在Objective-C中,message与方法的真正实现是在执行阶段绑定的,而非编译阶段。编译器会将消息发送转换成对objc_msgSend方法的调用。

objc_msgSend方法含两个必要参数:receiver、方法名(即:selector),如:

[receiver message]; 将被转换为:objc_msgSend(receiver, selector);

objc_msgSend方法也能hold住message的参数,如:

objc_msgSend(receiver, selector, arg1, arg2, …);

objc_msgSend方法会做按照顺序进行以下操作,以完成动态绑定:

  1. 查找selector所指代的程序(方法的真正实现即方法的名字)。因为不同类对同一方法有不同的实现,所以对方法的真正实现的查找依赖于receiver的类
  2. 调用该实现,并将一系列参数传递过去
  3. 将该实现的返回值作为自己的返回值,返回之

二、Object-c 运行时允许你修改selector(method name)到implementation(the method code itself)的映射。

在运行时,OC 的方法被称为一种叫 Method 的结构体,这种 objc_method 类型的结构体定义为

struct objc_method {

   SEL method_name         OBJC2_UNAVAILABLE;//方法的 selector,可以理解为运行时的方法名;

   char *method_types      OBJC2_UNAVAILABLE;//是一个参数和返回值类型编码的字符串

   IMP method_imp          OBJC2_UNAVAILABLE;//是指向方法实现的指针

}

当我们向一个receiver的一个方法selector发送消息时,而查找消息的唯一依据就是selector的名字,每个selector都有它自己影射的实现(这个实现我们可以通过class_getInstanceMethod或者class_getClassMethod方法获取,通过这两个方法获取回来的影射实现即为结构体Method);然后我们再通过 method_getImplementation获取IMP,selector和IMP这两部分是分开的,于是我们可以在程序运行的时候动态的改变方法名和方法实现之间的映射关系(即改变Method中的IMP),从而实现改变代码的实现过程。

三、具体代码:

@implementation NSArray (swizzle)

-(id)xxx_lastObject

{

id ret = [self xxx_lastObject];//被交换过后的xxx_lastObject实际上已经指向了lastObject,所以不存在递归问题

NSLog(@"ret======:%@",ret);

return ret;

}

Swizzling应该在+load方法中实现。

每个类的这两个方法会被Objective-C运行时系统自动调用,+load是在一个类最开始加载时调用,+initialize是在应用中第一次调用该类或它的实例的方式之前调用。这两个方法都是可选的,只有实现了才会被执行。

+(void)load

{

//获取lastObject方法的Method结构体

Method originalMdthod = class_getInstanceMethod([self class], @selector(lastObject));

//获取lastObject方法的Method结构体的IMP,仅用于观察

IMP originalIMP = method_getImplementation(originalMdthod);

//获取xxx_lastObject方法的Method结构体

Method myMethod = class_getInstanceMethod([self class], @selector(xxx_lastObject));

//获取xxx_lastObject方法的Method结构体的IMP,仅用于观察

IMP myIMP = method_getImplementation(myMethod);

//交换两个结构体originalMdthod和myMethod的IMP指针

method_exchangeImplementations(originalMdthod, myMethod);

//获取交换IMP指针后originalMdthod方法的Method结构体的IMP,仅用于观察

IMP originalIMPAfterSwizzle = method_getImplementation(originalMdthod);

//获取交换IMP指针后xxx_lastObject方法的Method结构体的IMP,仅用于观察

IMP myIMPAfterSwizzle = method_getImplementation(myMethod);

}

这是debug模式下在控制台下的打印:

Printing description of originalIMP:

(IMP) originalIMP = 0x2c5622cd (CoreFoundation`-[NSArray lastObject] + 1)

Printing description of originalIMPAfterSwizzle:

(IMP) myIMP = 0x000191e1 (swizzle`-[NSArray(swizzle) xxx_lastObject] + 1 at NSArray+swizzle.m)

Printing description of myIMPAfterSwizzle:

(IMP) originalIMPAfterSwizzle = 0x000191e1 (swizzle`-[NSArray(swizzle) xxx_lastObject] + 1 at NSArray+swizzle.m)

Printing description of myIMP:

(IMP) myIMPAfterSwizzle = 0x2c5622cd (CoreFoundation`-[NSArray lastObject] + 1)

由打印出来的结结果可以看到,交换后的originalIMPAfterSwizzle的IMP指向了myIMP,而myIMPAfterSwizzle指向了originalIMP

使用一下这个东西:

NSArray *arr = @[@"0",@"1",@"2",@"3",@"4"];

NSString *str = [arr lastObject];//被交换过后的lastObject实际上已经指向了xxx_lastObject

NSLog(@"test result:%@",str);

控制台下面的打印是这样的:

2014-10-08 12:25:25.314 swizzle[414:27576] ret======:4

2014-10-08 12:25:25.315 swizzle[414:27576] test result:4

有了Method Swizzling这个东西之后,你就可以"修补"那些没有源码的方法,如(AppKit,FoundationKit,或第三方的库等)。
和category不同的是,category如果定义了一个原来相同的方法,那么会直接覆盖原来的方法。Method Swizzling让你可以在替代原来的方法的同时可以调用原来的方法。有点像subclassing。

时间: 2024-11-10 13:19:32

初探Method Swizzling的相关文章

Objective-C Runtime 运行时之四:Method Swizzling

理解Method Swizzling是学习runtime机制的一个很好的机会.在此不多做整理,仅翻译由Mattt Thompson发表于nshipster的Method Swizzling一文. Method Swizzling是改变一个selector的实际实现的技术.通过这一技术,我们可以在运行时通过修改类的分发表中selector对应的函数,来修改方法的实现. 例如,我们想跟踪在程序中每一个view controller展示给用户的次数:当然,我们可以在每个view controller的

iOS 使用Method Swizzling隐藏Status Bar

在iOS 6中,隐藏Status Bar非常的简单. // iOS 6及以前,隐藏状态栏 [[UIApplication sharedApplication] setStatusBarHidden:YES]; 来到了iOS 7的年代以后,需要在UIViewController中指定: #ifdef __IPHONE_7_0 - (BOOL)prefersStatusBarHidden { return YES; } #endif 并通过下列代码刷新状态栏: if ([viewController

iOS Method Swizzling和分类的妙用AppDelegate轻量化处理

http://www.cocoachina.com/ios/20151117/14167.html 简介 在iOS工程中,AppDelegate往往会有上千行,甚至几千行,这样就会给维护AppDelegate带来诸多麻烦.比方说,老板想在出现HomeViewController之前弹出广告并停顿几秒,这样你就要加入插入广告的逻辑:又比方说,老板想在开始做个请求,判断某个开关是否打开.这样就会在AppDelegate中插入很多相关的不相关的代码. 在AppDelegate中,- (BOOL)app

Method Swizzling

[Cocoa]深入浅出Cocoa之 Method Swizzling cocoaclassinterfacestructmethodsapi [Cocoa]深入浅出Cocoa之 Method Swizzling 罗朝辉(http://blog.csdn.net/kesalin) CC许可,转载请注明出处 在前文深入浅出Cocoa之消息中,我简要介绍了ObjC 中消息的基本情况,包括SEL查找,缓存以及消息转发等.在本文中,我要介绍一个很有趣的技术,Method swizzling,通过这个手法,

ios method swizzling

阅读器 iOS开发iOS 本文由TracyYih[博客]翻译自NSHipster的文章Method Swizzling. 在上周associated objects一文中,我们开始探索Objective-C运行时的一些黑魔法.本周我们继续前行,来讨论可能是最受争议的运行时技术:method swizzling. Method swizzling指的是改变一个已存在的选择器对应的实现的过程,它依赖于Objectvie-C中方法的调用能够在运行时进改变——通过改变类的调度表(dispatch tab

iOS执行时与method swizzling

C语言是静态语言,它的工作方式是通过函数调用,这样在编译时我们就已经确定程序怎样执行的.而Objective-C是动态语言,它并不是通过调用类的方法来执行功能,而是给对象发送消息,对象在接收到消息之后会去找匹配的方法来执行.这样的做法就把C语言在编译时的工作挪到了执行时来做,能够获得额外的灵活性. 在Objective-C中有个@selector,在非常多地方被翻译成"选择子".实际上,对于类的实例对象来说,类的方法是用一个数字来代表的,并不是是我们看到的一个长长的带着:这个字符的一串

runtime 第四部分method swizzling

接上一篇 http://www.cnblogs.com/ddavidXu/p/5924597.html 转载来源http://www.jianshu.com/p/6b905584f536 http://southpeak.github.io/2014/10/30/objective-c-runtime-2/ runtime的黑魔法,就是可以实现交换两个方法的实现,这就意味着我们可以修改系统的方法实现. 栗子:当UIViewController及其子类的对象调用viewWillAppear时,都会

iOS黑魔法-Method Swizzling

公司年底要在新年前发一个版本,最近一直很忙,好久没有更新博客了.正好现在新版本开发的差不多了,抽空总结一下.由于最近开发新版本,就避免不了在开发和调试过程中引起崩溃,以及诱发一些之前的bug导致的崩溃.而且项目比较大也很不好排查,正好想起之前研究过的Method Swizzling,考虑是否能用这个苹果的“黑魔法”解决问题,当然用好这个黑魔法并不局限于解决这些问题...... 需求 就拿我们公司项目来说吧,我们公司是做导航的,而且项目规模比较大,各个控制器功能都已经实现.突然有一天老大过来,说我

什么是method swizzling(俗称黑魔法)

之前所说的消息转发虽然功能强大,但需要我们了解并且能更改对应类的源代码,因为我们需要实现自己的转发逻辑.当我们无法触碰到某个类的源代码,却想更改这个类某个方法的实现时,该怎么办呢?可能继承类并重写方法是一种想法,但是有时无法达到目的.这里介绍的是 Method Swizzling ,它通过重新映射方法对应的实现来达到"偷天换日"的目的.跟消息转发相比,Method Swizzling 的做法更为隐蔽,甚至有些冒险,也增大了debug的难度.这里摘抄一个 NSHipster 的例子: #