说说NSProxy
在Obective-C中,绝大部分的类都继承自NSObject,但是NSProxy确实例外, 它和NSObject一样,不继承自任何一个类。
那么NSProxy这个类是用来干嘛的?什么时候用到这个类?
我们对比一下NSProxy和NSObject的定义,可以发现,NSProxy有的属性、实例变量和方法,NSObject都有,并且都实现了NSObject协议。所以,从理论上来说,NSProxy能够实现的功能,NSObject都能完成。
那么为什么还需要这个类呢?这个问题先放在这里,我们先来看看一般用NSProxy做什么
一、使用NSProxy实现面向切面编程(AOP)
1、概念:AOP:AOP是一种编程范式,其目的是通过横切将功能与程序中的其他组成部分分开,提高程序的模块化程度。
2、实例:
假设有这么一个需求,我需要在一个系统类库中的某个实例方法方法调用前后做一些处理,我们想到的方式可能有三种:
a、直接在调用该方法前后做处理(缺点:每次调用都需要处理,麻烦、不利于代码重用、不优雅……)
b、写一个该类的子类,重载这个方法(缺点:对每一个有该需求的类都需要创建一个子类, 对每一个需要处理的方法都要重载)
c、写一个类别,通过runtime的方法交换来实现(缺点:和b一样,对每一个有该需求的类都需要创建一个分类, 对每一个需要处理的方法都需要交换)
下面我们来看通过AOP的方式实现:
// YQProxy.h #import <Foundation/Foundation.h> // 定义一个需要调用的block typedef void(^YQProxyBlock)(id targete, SEL selector); @interface YQProxy : NSProxy // 创建代理 + (id)proxyWithTarget:(id)target; // 对某个方法注册相应的block - (void)registerSelector:(SEL)selector withPreBlock:(YQProxyBlock)preBlock sufBlock:(YQProxyBlock)sufBlock; @end // YQProxy.m #import "YQProxy.h" @interface YQProxy () @property (nonatomic, strong) id targete; // 实际调用方法的对象 @property (nonatomic, strong) NSMutableDictionary<NSValue *, YQProxyBlock> *preBlocks; // 目标方法调用之前调用的block @property (nonatomic, strong) NSMutableDictionary<NSValue *, YQProxyBlock> *sufBlocks; // 目标方法调用之后调用的block @end @implementation YQProxy + (id)proxyWithTarget:(id)target{ YQProxy *proxy = [YQProxy alloc]; proxy.targete = target; proxy.preBlocks = [NSMutableDictionary dictionary]; proxy.sufBlocks = [NSMutableDictionary dictionary]; return proxy; } #pragma mark - override - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ return [self.targete methodSignatureForSelector:sel]; } // 进行消息转发 - (void)forwardInvocation:(NSInvocation *)invocation{ if (![self.targete respondsToSelector:invocation.selector]) { return; } NSValue *method = [NSValue valueWithPointer:invocation.selector]; YQProxyBlock preBlock = [self.preBlocks objectForKey:method]; YQProxyBlock sufBlock = [self.sufBlocks objectForKey:method]; if (preBlock) { preBlock(self.targete, invocation.selector); } [invocation invokeWithTarget:self.targete]; if (sufBlock) { sufBlock(self.targete, invocation.selector); } } #pragma mark - public - (void)registerSelector:(SEL)selector withPreBlock:(YQProxyBlock)preBlock sufBlock:(YQProxyBlock)sufBlock{ NSValue *method = [NSValue valueWithPointer:selector]; if (preBlock) { [self.preBlocks setObject:preBlock forKey:method]; }else if ([self.preBlocks objectForKey:method]){ [self.preBlocks removeObjectForKey:method]; } if (sufBlock) { [self.sufBlocks setObject:sufBlock forKey:method]; }else if ([self.sufBlocks objectForKey:method]){ [self.sufBlocks removeObjectForKey:method]; } } @end
方法调用
#import "YQProxy.h" int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... id array = [YQProxy proxyWithTarget:[NSMutableArray array]]; [array registerSelector:@selector(addObject:) withPreBlock:^(id targete, SEL selector) { NSLog(@"%@:准备插入一个数据到数组中", targete); } sufBlock:^(id targete, SEL selector) { NSLog(@"%@:插入数据完成", targete); }]; id string = [YQProxy proxyWithTarget:[NSMutableString stringWithString:@"hello"]]; [string registerSelector:@selector(appendString:) withPreBlock:^(id targete, SEL selector) { NSLog(@"%@:正在追加字符串", targete); } sufBlock:^(id targete, SEL selector) { NSLog(@"%@:追加字符串完成", targete); }]; [array addObject:@"你好"]; [string appendString:@", world!"]; } return 0; }
打印结果
2017-08-10 10:14:29.090402+0800 NSProxyTest[57689:7732479] ( ):准备插入一个数据到数组中 2017-08-10 10:14:29.090638+0800 NSProxyTest[57689:7732479] ( "\U4f60\U597d" ):插入数据完成 2017-08-10 10:14:29.090724+0800 NSProxyTest[57689:7732479] hello:正在追加字符串 2017-08-10 10:14:29.090752+0800 NSProxyTest[57689:7732479] hello, world!:追加字符串完成 Program ended with exit code: 0
可以看到,通过这种方式比较优雅的有较少的代码就完成了这种需求。
二、使用NSProxy模拟多继承
可是,Objective-C却不支持这样一个强大的特性。不过NSProxy可以帮我们在某种程度上(这只是一个模拟的多继承,并不是完全的多继承)解决这个问题:
现在假设我们想要去买书,但是我懒癌犯了,不想直接去书店(供应商)买,如果有一个跑腿的人(经销商)帮我去书店买完,我再跟他买。同时,我买完书又想买件衣服,我又可以很轻松地在他那里买到一件衣服(多继承)。
创建一个书店
//YQBookStore.h #import <Foundation/Foundation.h> @interface YQBookStore : NSObject - (void)fetchBookWithTitle:(NSString *)title; @end // YQBookStore.m #import "YQBookStore.h" @implementation YQBookStore - (void)fetchBookWithTitle:(NSString *)title{ if (!title) { NSLog(@"请确定书名"); return; } NSLog(@"这是您好的书:%@", title); } @end
创建一个服装店
// YQClothesStore.h #import <Foundation/Foundation.h> @interface YQClothesStore : NSObject - (void)buyCloths; @end // YQClothesStore.m #import "YQClothesStore.h" @implementation YQClothesStore - (void)buyCloths{ NSLog(@"您的衣服"); } @end
创建一个既能卖书、又能卖衣服的代理商
// YQStoreProxy.h #import <Foundation/Foundation.h> #import "YQBookStore.h" #import "YQClothesStore.h" @interface YQStoreProxy : NSProxy - (instancetype)init; @end // YQStoreProxy.m #import "YQStoreProxy.h" @interface YQStoreProxy () @property (nonatomic, strong) YQBookStore *bookStore; @property (nonatomic, strong) YQClothesStore *clothesStore; @end @implementation YQStoreProxy - (instancetype)init{ _bookStore = [[YQBookStore alloc] init]; _clothesStore = [[YQClothesStore alloc] init]; return self; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ return [[self targetWithSeletor:sel] methodSignatureForSelector:sel]; } - (void)forwardInvocation:(NSInvocation *)invocation{ [invocation invokeWithTarget:[self targetWithSeletor:invocation.selector]]; } #pragma mark - private - (id)targetWithSeletor:(SEL)selector{ if ([self.bookStore respondsToSelector:selector]) { return self.bookStore; }else if ([self.clothesStore respondsToSelector:selector]){ return self.clothesStore; } return nil; } @end
使用
int main(int argc, const char * argv[]) { @autoreleasepool { // insert code here... id store = [[YQStoreProxy alloc] init]; [store buyBookWithTitle:@"鬼吹灯"]; [store buyCloths]; } return 0; }
运行结果
2017-08-10 10:54:11.629856+0800 NSProxyTest[58903:7889918] 这是您好的书:鬼吹灯 2017-08-10 10:54:11.630060+0800 NSProxyTest[58903:7889918] 您的衣服
从上面的实例中可以看到,通过NSProxy结合消息转发机制, 很好的实现在面向切面编程和Objective-C的多继承。
所以, 我们常常通过NSProxy来转发消息。回到上面的问题,理论上,NSProxy能实现的功能,NSObject都能实现,而在上面的两个案例中,把NSProxy改为NSObject也完全没有问题。那么,为什么还需要NSProxy类呢?以下是知乎上的回答