runtime交换方法的正确姿势
说到Objective-C大家就会想到黑魔法runtime,(不知道runtime是什么的看这里,runtime是开源的,源码在这里),本文主要讲解如何利用runtime正确的交换方法,将会提到两种方式去交换,以及我们在什么情况下改怎么去用。
我们知道OC的方法到了runtime就被体现成结构体
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
要交换方法的话,无非也就是下面两个方法:
OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
我们通常同的估计也就是第二种方法吧,下面我们来看看用第二种方法存在什么样的问题,我们假设有这么两个方法:
Method m1 {
SEL method_name = @selector(originalMethodName)
char *method_types = “v@:“ //returns void, params id(self),selector(_cmd)
IMP method_imp = 0x000FFFF (MyBundle`[MyClass originalMethodName])
}
Method m2 {
SEL method_name = @selector(swizzle_originalMethodName)
char *method_types = “v@:”
IMP method_imp = 0x1234AABA (MyBundle`[MyClass swizzle_originalMethodName])
}
我们方法实现如下,在交换的方法里面调用原来的方法:
- (void) originalMethodName //m1
{
}
- (void) swizzle_originalMethodName //m2
{
[self swizzle_originalMethodName];//call original method
}
那么这两个方法看起来就是这样的:
Method m1 {
SEL method_name = @selector(originalMethodName)
char *method_types = “v@:“ //returns void, params id(self),selector(_cmd)
IMP method_imp = 0x1234AABA (MyBundle`[MyClass swizzle_originalMethodName])
}
Method m2 {
SEL method_name = @selector(swizzle_originalMethodName)
char *method_types = “v@:”
IMP method_imp = 0x000FFFF (MyBundle`[MyClass originalMethodName])
}
那么问题就来了,因为这样导致_cmd改变了,要是原来的方法根据_cmd做了操作的话(原方法实现一般都是看不到的),这时候就会出事了:
- (void) originalMethodName //m1
{
assert([NSStringFromSelector(_cmd) isEqualToString:@“originalMethodNamed”]);
}
那么我们在看看OBJC_EXPORT IMP method_setImplementation(Method m, IMP imp)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);这个方法来交换方法,该方法调用之后会返回原方法的实现,知道上面的问题之后,我们最好的交换方式就是用一个C语言函数实现去替换原方法实现,C函数大概长这样的:
void __Swizzle_OriginalMethodName(id self, SEL _cmd)
{
//调用原方法
originalImp(self,_cmd);
//code
}
那么我们下面的代码可能长这样:
IMP swizzleImp = (IMP)__Swizzle_OriginalMethodName;
IMP originalImp = method_setImplementation(method,swizzleImp);
拿这两个方法来说,主要就是防止人家的代码里面根据selector参数去做了操作而出现问题,这种情况多出现在你的代码用作三方等,要是自己项目的话,还是随便写的,不过说回来,要是苹果的方法里面这样用了,那么我们就不能用OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)
OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);交换哦
还是那句话,如您发现任何错误,欢迎留言指正~