一. NSProxy簡(jiǎn)介
NSProxy是一個(gè)抽象的超類绰上,它定義了一個(gè)對(duì)象的API癣诱,用來(lái)充當(dāng)其他對(duì)象或者一些不存在的對(duì)象的替身窖式。通常港柜,發(fā)送給Proxy的消息會(huì)被轉(zhuǎn)發(fā)給實(shí)際對(duì)象,或使Proxy加載(轉(zhuǎn)化為)實(shí)際對(duì)象胳赌。
NSProxy的子類可以用于實(shí)現(xiàn)透明的分布式消息傳遞(例如牢撼,NSDistantObject),或者用于創(chuàng)建開(kāi)銷較大的對(duì)象的惰性實(shí)例化
二. NSProxy應(yīng)用
- NSTimer解除循環(huán)引用
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.blueColor;
// self 強(qiáng)持有timer疑苫, timer中的target強(qiáng)持有self熏版,循環(huán)引用
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:nil repeats:YES];
}
- (void)test{
NSLog(@"1111");
}
- (void)dealloc{
[self.timer invalidate];
}
上的timer與controller循環(huán)引用,導(dǎo)致控制器無(wú)法無(wú)法釋放捍掺,有以下方式可以解除循環(huán)引用
1.使用block方式
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"1111");
}];
使用block的方式創(chuàng)建timer撼短,timer不持有target,不會(huì)造成循環(huán)引用
2.借助中間變量打破強(qiáng)引用
如圖1所示乡小,可以借助MyDelegate中間對(duì)象阔加,打破循環(huán)引用。
中間對(duì)象
@interface MyDelegate : NSObject
+ (instancetype)proxyWithTarget:(id)target;
@property (nonatomic, weak) id target;
@end
@implementation MyDelegate
// 初始化代理满钟,保存target
+ (instancetype)proxyWithTarget:(id)target
{
MyDelegate *delegate = [[MyDelegate alloc] init];
delegate.target = target;
return delegate;
}
// 運(yùn)行時(shí)動(dòng)態(tài)轉(zhuǎn)發(fā)消息胜榔,將消息轉(zhuǎn)交給self.target處理
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return self.target;
}
@end
使用中間對(duì)象
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[MyDelegate proxyWithTarget:self] selector:@selector(test) userInfo:nil repeats:YES];
當(dāng)執(zhí)行MyDelegate的test方式時(shí),通過(guò)isa指針去MyDelegate里面找test方法湃番,沒(méi)有找到夭织,在父類里面也沒(méi)有test方法,進(jìn)入動(dòng)態(tài)消息解析階段吠撮,也沒(méi)有處理尊惰,最后進(jìn)入消息轉(zhuǎn)發(fā)階段forwardingTargetForSelector,將消息轉(zhuǎn)發(fā)給tagget泥兰,此處的target是控制器弄屡,可以處理test消息。
3.使用NSProxy
原理與MyDelegate類似鞋诗,也是通過(guò)消息轉(zhuǎn)發(fā)膀捷,將test消息轉(zhuǎn)發(fā)給controller處理
@interface MyProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation MyProxy
+ (instancetype)proxyWithTarget:(id)target
{
MyProxy *proxy = [MyProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
[invocation invokeWithTarget:self.target];
}
@end
MyDelegate 與 MyProxy的區(qū)別
MyDelegate和 MyProxy都作為中間代理,MyDelegate集成自NSObject削彬,MyProxy集成自NSProxy
使用MyDelegate作為中間代理時(shí)全庸,調(diào)用test方法秀仲,會(huì)走完整的消息機(jī)制,尋找方法的路徑是: 當(dāng)前類緩存--> 當(dāng)前類----> 父類緩存---->父類---->動(dòng)態(tài)方法解析----->消息轉(zhuǎn)發(fā)壶笼。
使用MyProxy作為中間代理時(shí)神僵,調(diào)用test方式。尋找方法的路徑覆劈。
當(dāng)前類緩存--> 當(dāng)前類---->消息轉(zhuǎn)發(fā)
對(duì)于NSProxy保礼,接收 unknown selector 后,直接回調(diào)-methodSignatureForSelector:/-forwardInvocation:墩崩,消息轉(zhuǎn)發(fā)過(guò)程比class NSObject要簡(jiǎn)單得多
三.通過(guò)NSProxy在Objective-C中模擬多繼承
多繼承可以看作是單繼承的擴(kuò)展氓英。所謂多繼承是指派生類具有多個(gè)基類侯勉,派生類與每個(gè)基類之間的關(guān)系仍可看作是一個(gè)單繼承鹦筹。大家知道,Objective-C不支持多繼承址貌,但是NSProcy可以模擬多繼承铐拐。
@interface TargetProxy : NSProxy
{
id realObject1;
id realObject2;
}
-(id)initWithTarget1:(id)t1 target:(id)t2;
@end
@implementation TargetProxy
-(id)initWithTarget1:(id)t1 target:(id)t2
{
realObject1 = t1;
realObject2 = t2;
return self;
}
-(void)forwardInvocation:(NSInvocation *)invocation
{
id target = [realObject1 methodSignatureForSelector:invocation.selector]?realObject1:realObject2;
[invocation invokeWithTarget:target];
}
-(NSMethodSignature *)methodSignatureForSelector:(SEL)sel
{
NSMethodSignature *signature;
signature = [realObject1 methodSignatureForSelector:sel];
if (signature) {
return signature;
}
signature = [realObject2 methodSignatureForSelector:sel];
return signature;
}
-(BOOL)respondsToSelector:(SEL)aSelector
{
if ([realObject1 respondsToSelector:aSelector]) {
return YES;
}
if ([realObject2 respondsToSelector:aSelector]) {
return YES;
}
return NO;
}
@end
使用案例:
NSMutableArray *array = [NSMutableArray array];
NSMutableString *string = [NSMutableString string];
id proxy = [[TargetProxy alloc]initWithTarget1:array target:string];
[proxy appendString:@"This "];
[proxy appendString:@"is "];
[proxy addObject:string];
[proxy appendString:@"a "];
[proxy appendString:@"test!"];
NSLog(@"count should be 1,it is:%ld",[proxy count]);
if ([[proxy objectAtIndex:0] isEqualToString:@"This is a test!"]) {
NSLog(@"Appending successful: %@",proxy);
}else
{
NSLog(@"Appending failed, got: %@", proxy);
}
NSLog(@"Example finished without errors.");
//TargetProxy擁有了NSSting與NSArray倆個(gè)類的方法屬性
可以看到 TargetProxy擁有了NSSting與NSArray倆個(gè)類的方法屬性。類似于多繼承