一次方法调配的非理想使用
问题:
在已有工程中,如何实现在数组添加数据的同时打印此数据?
答案:
可以使用子类或方法调配(method swizzled),然而,对于本问题两者都不理想,旨在讨论使用方法调配时的考虑因素
讨论:
此问题最直观的思路为继承子类覆写addObject方法,但是对于已有的大型项目,很多地方使用了NSMutablearray,需要一一替换,显然并不现实。
可以使用运行时中的方法调配,方法调配的作用是将两个方法名对应的指针互换,从而实现,调用老方法(A)实现新方法(B)。同理,调用方法B,会实现方法A.
新方法可以使用分类来添加:
@implementation NSMutableArray (PrintandAddObject)
-(void)printAndAddObject:(id)object{
NSLog(@"添加数据:%@",object);
[self printAndAddObject:object];
}
@end
貌似递归,然而在运行期,经过方法调配后,printAndAddObject这个Method实际上对应于addObject:方法
在启动时进行方法调配,代码如下:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
Method addObject = class_getInstanceMethod([[NSMutableArray array] class], @selector(addObject:));
Method printAndAddObject = class_getInstanceMethod([NSMutableArray class], @selector(printAndAddObject:));
method_exchangeImplementations(addObject, printAndAddObject);
return YES;
}
代码添加完毕,有必要讲解一处知识点,如果细看两个Method的第一个参数,是不一样的,老方法的是[[NSMutableArray array] class],新方法的是[NSMutableArray class].
新方法是使用类目为NSMutalbeArray添加的,必然是NSMutalbeArray这个类的方法,使用没有歧义.
对于老方法为什么使用[[NSMutableArray array] class],而不是[NSMutableArray class]呢?这是因为两者获取到的类并不一样,[[NSMutableArray array] class]是获取到的实例化的对象的类,这个类并不是NSMutableArry 而是__NSArrayM,这个类并没有暴露给你,这就涉及到了类簇的概念。简单讲就是NSMutableArray是抽象类工厂,隐藏了很多真正的类。
这一点很重要,很多人初学方法调配时,很有可能调入这个坑里,所以特意提出。
到此为止,貌似可以实现需求,但是前面说过这个效果也并不理想,为什么这么说呢?因为系统很多地方用到了NSMutablearray,譬如手势,Gesture,等,这样控制台会打印很多噪音,使我们想要的东西埋没其中。我拷贝一份打印信息,放在最后,可以参考。
小小总结:正常使用方法调配,是能实现个性化需求的,但是一些类,并非只有我们手动调用,内部也会调用,使用时,需要考虑一下。
以下是点击button,为一个数组添加字符串@“####first”时的打印,确实打印了相关数据:
2016-07-06 00:08:06.991 PrintData[18983:1725090]添加数据:####first
但是由于系统调用的缘故,噪音太多,所以说使用效果不理想
2016-07-06 00:08:06.883 PrintData[18983:1725090] 添加数据:<UIStatusBarWindow: 0x7f8922c18ab0; frame = (0 0; 414 736); opaque = NO; gestureRecognizers = <NSArray: 0x7f8922c1e7e0>; layer = <UIWindowLayer: 0x7f8922c1a3d0>>
2016-07-06 00:08:06.883 PrintData[18983:1725090] 添加数据:<UIWindow: 0x7f8922f11000; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x7f8922f123c0>; layer = <UIWindowLayer: 0x7f8922f10670>>
2016-07-06 00:08:06.885 PrintData[18983:1725090] 添加数据:<UIStatusBar: 0x7f8924801000; frame = (0 0; 414 20); opaque = NO; autoresize = W+BM; layer = <CALayer: 0x7f8922f04df0>>
2016-07-06 00:08:06.886 PrintData[18983:1725090] 添加数据:<UITouchData: 0x7f8922e8d020>
2016-07-06 00:08:06.887 PrintData[18983:1725090] 添加数据:<_UISystemGestureGateGestureRecognizer: 0x7f8922f118e0; state = Possible; delaysTouchesEnded = NO; view = <UIWindow 0x7f8922f11000>>
2016-07-06 00:08:06.887 PrintData[18983:1725090] 添加数据:<_UISystemGestureGateGestureRecognizer: 0x7f8922f11e70; state = Possible; delaysTouchesBegan = YES; delaysTouchesEnded = NO; view = <UIWindow 0x7f8922f11000>>
2016-07-06 00:08:06.890 PrintData[18983:1725090] 添加数据:<UITouch: 0x7f8922f16fd0> phase: Began tap count: 1 window: <UIWindow: 0x7f8922f11000; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x7f8922f123c0>; layer = <UIWindowLayer: 0x7f8922f10670>> view: <UIButton: 0x7f8922c0b5c0; frame = (100 100; 100 100); opaque = NO; layer = <CALayer: 0x7f8922c20550>> location in window: {139, 167} previous location in window: {139, 167} location in view: {39, 67} previous location in view: {39, 67}
2016-07-06 00:08:06.891 PrintData[18983:1728813] 添加数据:<__NSMallocBlock__: 0x7f8922d25560>
2016-07-06 00:08:06.900 PrintData[18983:1725090] 添加数据:<UIStatusBarWindow: 0x7f8922c18ab0; frame = (0 0; 414 736); opaque = NO; gestureRecognizers = <NSArray: 0x7f8922c1e7e0>; layer = <UIWindowLayer: 0x7f8922c1a3d0>>
2016-07-06 00:08:06.900 PrintData[18983:1725090] 添加数据:<UIWindow: 0x7f8922f11000; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x7f8922f123c0>; layer = <UIWindowLayer: 0x7f8922f10670>>
2016-07-06 00:08:06.951 PrintData[18983:1725090] 添加数据:<UIStatusBarWindow: 0x7f8922c18ab0; frame = (0 0; 414 736); opaque = NO; gestureRecognizers = <NSArray: 0x7f8922c1e7e0>; layer = <UIWindowLayer: 0x7f8922c1a3d0>>
2016-07-06 00:08:06.951 PrintData[18983:1725090] 添加数据:<UIWindow: 0x7f8922f11000; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x7f8922f123c0>; layer = <UIWindowLayer: 0x7f8922f10670>>
2016-07-06 00:08:06.991 PrintData[18983:1725090] 添加数据:####first
2016-07-06 00:08:06.991 PrintData[18983:1725090] 添加数据:<UITouch: 0x7f8922c0c6f0> phase: Ended tap count: 1 window: <UIWindow: 0x7f8922f11000; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x7f8922f123c0>; layer = <UIWindowLayer: 0x7f8922f10670>> view: <UIButton: 0x7f8922c0b5c0; frame = (100 100; 100 100); opaque = NO; layer = <CALayer: 0x7f8922c20550>> location in window: {139, 167} previous location in window: {139, 167} location in view: {39, 67} previous location in view: {39, 67}