
NSProxy
前两天被问到有没有接触过NSHashTable and NSMapTable,由于项目中没用到过,所以之前也没有去了解过。然后,就在网上查了一下,结果在一篇文章中看到了一段话如下:
如果一个开发者想要存储一个weak类型的值或者使用一个没有实现NSCopying协议的object作为NSDictionary的key,他可能会很聪明的想到NSValue +valueWithNonretainedObject。
于是乎又去看看了valueWithNonretainedObject相关的内容,然后在这一篇文章中看到了本文的主角NSProxy。
偷窥NSProxy
NSProxy是和NSObject同级的一个类,可以说他是一个虚拟类,它只是实现了NSObject的协议。它的作用有点类似于一个复制的类。
我们可以看一下NSProxy里包含哪些内容:

可以看得出来,它确实遵守了NSObject协议,而且第一个Ivr是一个isa指针,所以我们可以把它当作一个NSObject或者派生类来使用。
通过代码加深偷窥
我们可以通过代码来看看,我么可以做哪些操作,可以用来做些什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
   | //  XZFProxy.h #import <Foundation/Foundation.h>
  @interface XZFProxy : NSProxy @property (nonatomic, copy) NSString *className; //类名,用来过滤类 - (void)initWithFromObjc:(NSObject *)objc; //初始化方法 @end
 
  //  XZFProxy.m #import "XZFProxy.h"
  @interface XZFProxy() @property (nonatomic, strong) NSObject *objc; @end
  @implementation XZFProxy
  + (id)proxyWithObjc:(NSObject *)objc {     XZFProxy *xzfProxy = [XZFProxy alloc];     xzfProxy.objc = objc;     return xzfProxy; } /**  初始化
   @param objc 传入一个objc  */ - (void)changeObjc:(NSObject *)objc {     self.objc = objc; }
  #pragma mark - 需要实现的方法 ///1.查询该方法的方法签名,用来生成 NSInvocation - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {     NSMethodSignature * mSignature = nil;     if ([self.objc methodSignatureForSelector:sel]) {         mSignature = [self.objc methodSignatureForSelector:sel];     } else {         mSignature = [super methodSignatureForSelector:sel];     }          return mSignature; }
  ///2.有了方法签名,调用方法实现 -(void)forwardInvocation:(NSInvocation *)invocation {     //在这里我们可以实现对任意对象的行为进行拦截     if (self.objc) {         //拦截方法的执行者为复制的对象         [invocation setTarget:self.objc];                  if ([self.objc isKindOfClass:[NSClassFromString(self.className) class]]) {             //拦截参数 argument:表示的是方法的参数 index:表示的是方法参数的下标             NSString *str = @"不爱学习,只想玩。。。";             [invocation setArgument:&str atIndex:3];         }                  //开始调用方法         [invocation invoke];     } } @end
   | 
 
代码的调用:
1 2 3 4 5 6 7 8 9 10 11 12
   | - (void)viewDidLoad {     [super viewDidLoad];          XZFTeacher *teacher = [[XZFTeacher alloc] init];     XZFProxy *xzfProxy = [XZFProxy proxyWithObjc:teacher];     xzfProxy.className = @"XZFTeacher";     [xzfProxy performSelector:@selector(callStudentWithName:toLearnProject:) withObject:@"张三" withObject:@"英语"];          XZFStudent *student = [[XZFStudent alloc] initWithName:@"李四"];     [xzfProxy changeObjc:student];     [xzfProxy performSelector:@selector(startLearnProject:) withObject:@"数学"]; }
  | 
 
我们可以在控制台查看打印的结果:

我们很轻松的改变了Teacher的执行内容。
当然我们还可以通过下面两个方法,处理一个方法的返回值:
1 2
   | - (void)getReturnValue:(void *)retLoc; - (void)setReturnValue:(void *)retLoc;
   | 
 
我们可以用下面的代码来获取被代理对象的方法返回值:
1 2
   | NSString *str; [invocation setReturnValue:&str];
   | 
 
通过下面的代码来修改被代理对象的方法的返回值:
1 2
   | NSString *str = @"迎娶白富美,出任CEO"; [invocation setReturnValue:&str];
   | 
 
值得注意的是:上述方法是拷贝指针所指向的数据,所以要传递str指针的指针,这样才能把str设置为返回值的地址,切记不要弄混淆了。
总结
通过NSProxy,不仅可以修改方法的执行结果,还可以实现埋点计数等功能。你可以开动你的大脑去想想怎么利用NSProxy实现更多你想实现的效果。本文代码地址请戳这里。想了解更多的内容你可以参考下面的几篇文章:
- https://www.jianshu.com/p/a7187e014c03
 
- https://www.jianshu.com/p/923f119333d8
 
- https://blog.csdn.net/ssirreplaceable/article/details/53375972