Method Swizzling替换子类中未重写的父类方法实现

当用MethodSwizzling替换子类中未重写的父类方法实现时,class_getInstanceMethod会获取到子类没有重写的父类方法的Method,这个Method对应的是父类中的方法,用这个Method调用method_exchangeImplementations实现MethodSwizzling之后就会交换父类Method和子类Method的实现。然后在基类对象上调用被交换的方法时,如果该方法调用了自身就会引起unrecognizedselector异常。
提问后追加:1.我知道unrecognizedselector异常出现的原因;2.“这种情况”指的是子类没有重写父类的方法,但是代码中又需要针对这个特定子类及其派生类重写方法实现的情况。
具体情景可以参照:需要在UITableViewCell类及其子类上重写NSObject实现的方法setValue:forKeyPath:,而又不影响UITableViewCell的基类的方法调用结果。
问:在这种情况下,如何“更优美”地达到重写不能修改源码的特定方法的目的呢?
示例代码如下:
#类声明
@interfaceBaseClass:NSObject
-(void)baseVersionMethod;
@end
@interfaceSubClass:BaseClass
@end
@interfaceSubClass(MySubClass)
-(void)myMethod;
@end
#类定义
@implementationBaseClass
-(void)baseVersionMethod{
NSLog(@"baseVersionMethodhasbeencalled.");
}
@end
@implementationSubClass
@end
@implementationSubClass(MySubClass)
-(void)myMethod{
NSLog(@"myMethod");
[selfmyMethod];
}
+(void)load{
MethodoriM=class_getInstanceMethod(SubClass.class,@selector(baseVersionMethod));
MethodnewM=class_getInstanceMethod(SubClass.class,@selector(myMethod));
method_exchangeImplementations(oriM,newM);
}
@end
#调用交换后的方法
intmain(intargc,char*argv[]){
BaseClass*obj=[[BaseClassalloc]init];
[objbaseVersionMethod];//抛出unrecognizedselector异常
}
湖上湖
浏览 234回答 2
2回答

慕的地10843

父类没有实现子类的方法,当然会抛异常啊。可以在父类的类别里实现@interfaceBaseClass(Swizzle)-(void)myMethod;@end@implementationBaseClass(Swizzle)-(void)myMethod{NSLog(@"myMethod");[selfmyMethod];}+(void)load{MethodoriM=class_getInstanceMethod(BaseClass.class,@selector(baseVersionMethod));MethodnewM=class_getInstanceMethod(BaseClass.class,@selector(myMethod));method_exchangeImplementations(oriM,newM);}@end

当年话下

MethodSwizzling的原理Methodswizzling的原理是改变在方法映射表中SEL与IMP的对应关系,所以,写method应该在改变对应关系上下手。贴出代码的问题所在class_getClassMethod这个方法是返回指向方法的指针,而不是方法返回方法的implementation。你应该用method_getImplementation这个方法。IMPimp1=method_getImplementation(m1);IMPimp2=method_getImplementation(m2);method_setImplementation(m1,imp2);method_setImplementation(m2,imp1);另一种实现SELselector=@selector(printHello);IMPselectorImplementation=imp_implementationWithBlock(^{NSLog(@"HI");});MethodtransformedValueClassMethod=class_getClassMethod([testClassclass],selector);class_replaceMethod([testClassclass],selector,selectorImplementation,method_getTypeEncoding(transformedValueClassMethod));补充修改===========================================之前没有理解题主的意思,以为题主是问methodswizzling的相关问题,就匆匆写了上面的答案,现在细看题目之后回答:出现unrecognizedselector的原因题主交换了myMethod和baseVersionMethod的IMP,因此,在调用baseVersionMethod的时候调用的是-(void)myMethod{NSLog(@"myMethod");[selfmyMethod];}的implementation。此时的baseVersionMethod是这样的:-(void)baseVersionMethod{NSLog(@"myMethod");[selfmyMethod];}注意,代码块中[selfmyMethod]一句中在baseClass实例里面调用的话是没有myMethod的selector的,所以报错。unrecognizedselector是指myMethod而不是baseVersionMethod。methodswizzling最好用在同一个类中的方法,因为如果跨类替换的话容易出现implementation中的实例变量,或者调用方法和实例内容不符的情况,就像你代码中出现的问题一样。这非常容易导致crash,最惨的是如果没有crash,后面会产生各种稀奇古怪的bug,很难调试。如何达到重写不修改源码特定方法的目的这句话的意思是如何在原方法被swizzling之后再去调用原方法,返回结果不变吗?没看明白你这个问题,现在姑且猜测你就是这个意思吧。在同一个类中:-(void)swizzlingMethod{[selfswizzlingMethod];}-(void)swizzledMethod{NSLog(@"swizzled");}虽然swizzling和swizzled方法内容被交换,但是在调用swizzled方法的时候implementation又调了一遍swizzled的implementation,效果和直接调用交换前的swizzled方法等效。该不该用methodswizzling?methodswizzling就像一把快刀,有人觉得这刀太锋利了,容易伤人,但也有人就喜欢用快刀。对于runtime的世界,没有不能触碰的禁区。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript