一、来自官方文档的介绍
一个抽象的超类,它定义了对象的API,充当其他对象或不存在的对象的替身。
通常,将消息发送到proxy,然后将其转发给实际对象,或使proxy加载(或转换为)实际对象。NSProxy的子类可以用于实现透明的分布式消息传递(例如,NSDistantObject),或者用于创建开销较大的对象的惰性实例化。NSProxy实现了根类所需的基本方法,包括在NSObject协议中定义的方法。但是,作为一个抽象类,它不提供初始化方法,并且在接收到它没有响应的消息时引发异常。因此,一个具体的子类必须提供一个初始化或创建方法,并重写forwardInvocation:和methodSignatureForSelector:来处理proxy它自己没有实现的方法。一个子类重写的forwardInvocation:应该处理所有调用,比如通过网络转发调用或加载真正能响应的对象并将调用转发过去。methodSignatureForSelector:要求为给定的消息提供参数类型信息;一个子类的实现应该能够确定要转发的消息的参数类型,并相应地构造一个NSMethodSignature对象。
翻译的更直白一点就是:
proxy可以实现代理对象转发消息。它相比NSObject类来说更轻量级,基于它能够将自身无法处理的消息转发给实际对象,我们可以利用这一点间接的实现多重继承的功能,或者拦截方法,在方法调用之前或是之后做一些工作。
二、那么究竟如何使用呢?
Download Sample Code
场景一: A类负责采购,B类负责财务报销。突然有一天PM说我想让C既能采购又能做财务报销。RD内心是崩溃的,两个完全不沾边的类怎么融合到一起,多继承?OC?不存在的!但是如果修改A或者B那工作量会很大。怎么办?!!
此时NSProxy闪亮亮的登场了。
1、模拟多继承
A类:
#import <Foundation/Foundation.h>
@interface A : NSObject
//采购
- (void)purchaseSomething;
@end
#import "A.h"
@implementation A
- (void)purchaseSomething{
NSLog(@"A类:负责采购");
}
@end
B类:
#import <Foundation/Foundation.h>
@interface B : NSObject
//财务报销
- (void)applyForReimbursement;
@end
#import "B.h"
@implementation B
- (void)applyForReimbursement{
NSLog(@"B类:负责财务报销");
}
@end
多面手C类:
#import <Foundation/Foundation.h>
@interface C : NSProxy
- (instancetype)initWithTarget1:(id)aObject target2:(id)bObject;
@end
#import "C.h"
@interface C()
{
id _object1;
id _object2;
}
@end
@implementation C
- (instancetype)initWithTarget1:(id)aObject target2:(id)bObject{
_object1 = aObject;
_object2 = bObject;
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
//返回一个方法签名
NSMethodSignature * sig = nil;
sig = [_object1 methodSignatureForSelector:sel];
if (sig) return sig;
sig = [_object2 methodSignatureForSelector:sel];
return sig;
}
//上个方法返回的方法签名会被包装成NSInvocation,invocation封装了未知消息所有相关细节的对象
- (void)forwardInvocation:(NSInvocation *)invocation{
id target = [_object1 methodSignatureForSelector:[invocation selector]] ? _object1 : _object2;
[invocation invokeWithTarget:target];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
if ([_object1 respondsToSelector:aSelector]) return YES;
if ([_object2 respondsToSelector:aSelector]) return YES;
return NO;
}
@end
测试一下
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
A * aObject = [[A alloc] init];
B * bObject = [[B alloc] init];
id cObject = [[C alloc] initWithTarget1:aObject target2:bObject];
NSLog(@"C类 开工喽~");
[cObject purchaseSomething];
[cObject applyForReimbursement];
}
2018-04-28 15:27:20.590332+0800 proxyDemo[7430:37679497] C类 开工喽~
2018-04-28 15:27:20.590485+0800 proxyDemo[7430:37679497] A类:负责采购
2018-04-28 15:27:20.590587+0800 proxyDemo[7430:37679497] B类:负责财务报销
是不是很神奇 _
如果看过iOS Runtime 运行时之 消息转发文章的朋友很容易理解。这一切都是runtime 消息转发的功劳。
场景二:
NSTimer 是开发中最常见的定时器,但是我们都知道如果将当前控制器(self)设置成target,想在delloc方法中调用[timer invalidate] 停止定时操作是不可能的,因为timer会强引用self,self也强引用timer。形成了循环引用,所以delloc根本不会被调用。导致内存泄漏。
一种解决办法是,自己判断时机,在不用定时器的时候[timer invalidate]。天知道,这并不容易。那么有什么更好的解决办法呢?
循环引用造成内存泄漏,那打破循环引用不就好了!
2、解决NSTimer内存泄漏问题
#import <Foundation/Foundation.h>
@interface TimerProxy : NSProxy
- (instancetype)transformTarget:(id)target;
@end
#import "TimerProxy.h"
@interface TimerProxy()
//注意weak 不然还是会造成循环引用
@property (nonatomic, weak) id object;
@end
@implementation TimerProxy
- (instancetype)transformTarget:(id)target{
self.object = target;
return self;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
//返回一个方法签名
NSMethodSignature * sig = nil;
sig = [self.object methodSignatureForSelector:sel];
return sig;
}
//上个方法返回的方法签名会被包装成NSInvocation,invocation封装了未知消息所有相关细节的对象
- (void)forwardInvocation:(NSInvocation *)invocation{
[invocation invokeWithTarget:self.object];
}
@end
#import "TestViewController.h"
#import "TimerProxy.h"
@interface TestViewController ()
@property (nonatomic, strong) NSTimer * timer;
@property (nonatomic, assign) NSInteger sec;
@end
@implementation TestViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.sec = 0;
id proxy = [[TimerProxy alloc] transformTarget:self];
self.timer = [NSTimer timerWithTimeInterval:1 target:proxy selector:@selector(timerAction) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:(NSRunLoopCommonModes)];
}
- (void)timerAction{
NSLog(@"%ld秒",++self.sec);
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
NSLog(@"TestViewController 销毁了");
}
2018-04-28 17:36:41.970992+0800 proxyDemo[16667:37846740] 1秒
2018-04-28 17:36:42.970643+0800 proxyDemo[16667:37846740] 2秒
2018-04-28 17:36:43.970988+0800 proxyDemo[16667:37846740] 3秒
2018-04-28 17:36:49.987038+0800 proxyDemo[16667:37846740] TestViewController 销毁了
以上你都明白了吗?欢迎留言批评指正。如果喜欢请给个赞_