1 、Swizzling是通过使用另一个方法替换该方法的实现来更改方法功能的行为,通常是在运行时。使用swizzling有许多不同的原因:内省、重写默认行为,甚至可能是动态方法加载。我看到很多博客文章讨论Objective-C中的swizzling,其中很多都推荐了一些非常糟糕的做法。如果您正在编写独立的应用程序,这些糟糕的做法其实并不是什么大问题,但是如果您正在为第三方开发人员编写框架,swizzling可能会打乱一些保持一切顺利运行的基本假设。那么,在Objective-C中使用swizzle的正确方法是什么?

让我们从基础开始。当我说swizzling时,我指的是用我自己的方法替换原始方法的行为,通常是从替换方法中调用原始方法。Objective-C允许使用Objective-C-runtime提供的函数执行此操作。在运行时,Objective-C方法表示为一个名为Method的C结构;

2、知识点

SEL : 类成员方法的指针,但不同于C语言中的函数指针,函数指针直接保存了方法的地址,但SEL只是方法编号。其中@selector()是取类方法的编号,取出的结果是SEL类型,​​SEL methodId = @selector(func);​

IMP:一个函数指针,保存了方法的地址



-(void)addNewMethod:(NSString *)str{}

取得SEL的方法

SEL sel = @selector(addNewMethod:);

取得方法名(编号)

NSString *methodName1 = NSStringFromSelector(@selector(addNewMethod:));

取得IMP指针

IMP imp = [self methodForSelector:@selector(addNewMethod:)]
或者
IMP imp = [NSObject instanceMethodForSelector:@selector(addNewMethod:)]


 



3 、相关的API:

利用​​method_exchangeImplementations​​来交换2个方法中的IMP。

利用 ​​class_replaceMethod​​来修改类。

利用 ​​method_setImplementation​​来直接设置某个方法的IMP。

方法交换类的实现:



/** 
* Sets the implementation of a method.
*
* @param m The method for which to set an implementation.
* @param imp The implemention to set to this method.
*
* @return The previous implementation of the method.
*/
OBJC_EXPORT IMP _Nonnull
method_setImplementation(Method _Nonnull m, IMP _Nonnull imp)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

/**
* Exchanges the implementations of two methods.
*
* @param m1 Method to exchange with second method.
* @param m2 Method to exchange with first method.
*
* @note This is an atomic version of the following:
* \code
* IMP imp1 = method_getImplementation(m1);
* IMP imp2 = method_getImplementation(m2);
* method_setImplementation(m1, imp2);
* method_setImplementation(m2, imp1);
* \endcode
*/
OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);
method_exchangeImplementations就相当于调用了两次method_setImplementation


4、下边是替换测试demo:




#import "ViewController.h"
#import <objc/message.h>
@interface ViewController ()
- (void) swizzleExample;
- (int) originalMethod;
@end

static IMP __original_Method_Imp;
int _replacement_Method(id self, SEL _cmd)//参数没有也行,所有的OC方法传递了两个隐藏的参数:对self的引用(id self),和方法名selector(SEL _cmd)
{


     assert([NSStringFromSelector(_cmd) isEqualToString:@"originalMethod"]);cmd是

originalMethod,这里没有跳断言。所以我们改变的只是IMP

printf("2222222222");
return 2;
}




@implementation ViewController

- (void) swizzleExample //call me to swizzle
{
Method m = class_getInstanceMethod([self class],
@selector(originalMethod));

__original_Method_Imp = method_setImplementation(m,
(IMP)_replacement_Method);//单边替换
}

- (int) originalMethod
{

printf("11111111111");
return 1;
}



- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.


int originalReturn = [self originalMethod];
[self swizzleExample];
int swizzledReturn = [self originalMethod];//最终调用的是22222
assert(originalReturn == 1); //true
assert(swizzledReturn == 2); //true
}


@end