平时开发中我们使用的大部分类的基类都是NSObject,今天介绍另一个基类——NSProxy。
先来看一下苹果官方文档:
NSProxy
An abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet.
好的,我们知道了他是一个抽象类。
再往下
Overview
Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy
can be used to implement transparent distributed messaging (for example, NSDistantObject
) or for lazy instantiation of objects that are expensive to create.
NSProxy
implements the basic methods required of a root class, including those defined in the NSObject
protocol. However, as an abstract class it doesn’t provide an initialization method, and it raises an exception upon receiving any message it doesn’t respond to. A concrete subclass must therefore provide an initialization or creation method and override the forwardInvocation:
and methodSignatureForSelector:
methods to handle messages that it doesn’t implement itself. A subclass’s implementation of forwardInvocation:
should do whatever is needed to process the invocation, such as forwarding the invocation over the network or loading the real object and passing it the invocation. methodSignatureForSelector:
is required to provide argument type information for a given message; a subclass’s implementation should be able to determine the argument types for the messages it needs to forward and should construct an NSMethodSignature
object accordingly. See the NSDistantObject
, NSInvocation
, and NSMethodSignature
class specifications for more information.
原来,它的作用是一个映射,通过定义子类,并重写
- (void)forwardInvocation:(NSInvocation *)anInvocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel;
这两个方法来实现将消息转发给真正的对象。我们知道OC不支持多继承,通过NSProxy,就可以模拟实现多继承。那么现在,NSProxy怎么用呢?
假设现在有这样一个需求:
我们已经将项目中的网络接口进行了模块化,将不同模块下的接口放在了不同的文件中。当我们想调用不同模块下的接口时,想要通过一个统一的映射来调用,现在我们来写这个映射。
首先,我们来创建两个接口模块。
商品模块:
@protocol ProductServiceProtocel <NSObject>
- (void)getProductInfo:(NSString *)productSkn;
@end
@interface ProductService : NSObject
@end
以及订单模块:
@protocol OrderServiceProtocel <NSObject>
- (void)submitOrder:(NSString *)prodcutName;
@end
@interface OrderService : NSObject
@end
注意:这里的接口声明要写在protocol中,然后让我们的proxy遵循这两个协议,用来骗过编译器。
然后我们实现这两个接口模块:
@implementation ProductService
- (void)getProductInfo:(NSString *)productSkn {
NSLog(@"我是一件程序员标配的横条纹T,我的skn是%@",productSkn);
}
@end
@implementation OrderService
- (void)submitOrder:(NSString *)prodcutName {
NSLog(@"我买了一件%@",prodcutName);
}
@end
现在我们来写我们的映射。
#import <Foundation/Foundation.h>
#import "ProductService.h"
#import "OrderService.h"
@interface ServiceProxy : NSProxy <ProductServiceProtocel, OrderServiceProtocel>
+ (ServiceProxy *)shareProxy;
@end
NSProxy是一个抽象类,系统不提供init方法,所以需要我们自己实现。
#import "ServiceProxy.h"
#import <objc/runtime.h>
@implementation ServiceProxy
{
ProductService *_product;
OrderService *_order;
NSMutableDictionary *_targetProxy;
}
#pragma class method
+ (ServiceProxy *)shareProxy {
return [[ServiceProxy alloc] init];
}
#pragma init
- (ServiceProxy *)init {
_targetProxy = [NSMutableDictionary dictionary];
_product = [[ProductService alloc] init];
_order = [[OrderService alloc] init];
[self _registerMethodsWithTarget:_product];
[self _registerMethodsWithTarget:_order];
return self;
}
在init方法中,初始化成员变量已经将各接口模块中的方法以及对象映射在一个字典中。
#pragma private
- (void)_registerMethodsWithTarget:(id)target {
unsigned int methodsNum = 0;
Method *methodList = class_copyMethodList([target class], &methodsNum);
for (int i = 0; i < methodsNum; i++) {
Method method = methodList[i];
SEL temp_sel = method_getName(method);
const char *temp_method_name = sel_getName(temp_sel);
[_targetProxy setObject:target forKey:[NSString stringWithUTF8String:temp_method_name]];
}
free(methodList);
}
接下来就可以重写系统提供的两个方法,根据方法名从我们的映射字典中找到对应的target,然后执行。
#pragma override
- (void)forwardInvocation:(NSInvocation *)invocation {
SEL selector = invocation.selector;
NSString *methodName = NSStringFromSelector(selector);
id target = _targetProxy[methodName];
if (target && [target respondsToSelector:selector]) {
[invocation invokeWithTarget:target];
}else {
[super forwardInvocation:invocation];
}
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
NSString *methodName = NSStringFromSelector(sel);
id target = _targetProxy[methodName];
if (target && [target respondsToSelector:sel]) {
return [target methodSignatureForSelector:sel];
}else {
return [super methodSignatureForSelector:sel];
}
}
现在服务映射就写完了,在控制器中来调动接口:
ServiceProxy *proxy = [ServiceProxy shareProxy];
[proxy getProductInfo:@"123456"];
[proxy submitOrder:@"程序员标配的横条纹T"];
最终的执行结果:demo