關(guān)鍵詞:NSProxy
,NSObject
,Runtime
面試題:
1)知道NSProxy嗎苟鸯?
2)NSProxy和NSObject的區(qū)別是什么问欠?
3)在開(kāi)發(fā)中NSProxy有哪些運(yùn)用場(chǎng)景稽荧?
一擦盾、什么是NSProxy
NSProxy is an abstract superclass defining an API for objects that act as stand-ins for other objects or for objects that don’t exist yet. 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是一個(gè)抽象的超類鳄袍,為充當(dāng)其他對(duì)象或尚不存在的對(duì)象的代理對(duì)象定義API窗怒。通常映跟,發(fā)送給代理的消息被轉(zhuǎn)發(fā)到實(shí)際對(duì)象,或者導(dǎo)致代理加載(或轉(zhuǎn)換為)真實(shí)對(duì)象扬虚。NSProxy的子類可用于實(shí)現(xiàn)透明的分布式消息傳遞(例如努隙,NSDistantObject)或用于延遲實(shí)例化創(chuàng)建代價(jià)高昂的對(duì)象。
NSProxy 是一個(gè)類似于NSObject的基類辜昵,是一等公民荸镊。
NS_ROOT_CLASS
@interface NSProxy <NSObject>{
Class isa;
}
二、NSProxy的用法
NSProxy實(shí)現(xiàn)了包括NSObject協(xié)議在內(nèi)基類所需的基礎(chǔ)方法,但是作為一個(gè)抽象的基類并沒(méi)有提供初始化的方法躬存。它接收到任何自己沒(méi)有定義的方法他都會(huì)產(chǎn)生一個(gè)異常收厨,所以一個(gè)實(shí)際的子類必須提供一個(gè)初始化方法或者創(chuàng)建方法,并且重載forwardInvocation:
方法和methodSignatureForSelector:
方法來(lái)處理自己沒(méi)有實(shí)現(xiàn)的消息优构。這也是NSProxy的主要功能诵叁,負(fù)責(zé)把消息轉(zhuǎn)發(fā)給真正的target的代理類,NSProxy正是代理的意思钦椭。
創(chuàng)建一個(gè)SFProxy類繼承于NSProxy:
// .h
@interface SFProxy : NSProxy
- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end
// .m
#import "SFProxy.h"
@interface SFProxy ()
@property (nonatomic, weak, readonly) NSObject *target;
@end
@implementation SFProxy
- (instancetype)initWithTarget:(id)target {
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target {
return [[self alloc] initWithTarget:target];
}
// 消息轉(zhuǎn)發(fā)
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (self.target && [self.target respondsToSelector:aSelector]) {
return [self.target methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL aSelector = [anInvocation selector];
if (self.target && [self.target respondsToSelector:aSelector]) {
[anInvocation invokeWithTarget:self.target];
} else {
[super forwardInvocation:anInvocation];
}
}
@end
使用場(chǎng)景:
1拧额、解決NSTimer/CADisplayLink的循環(huán)引用問(wèn)題
NSTimer是一個(gè)需要添加到Runloop里的類,對(duì)于一個(gè)不會(huì)自動(dòng)停止的Timer彪腔,你需要調(diào)用invalidate方法來(lái)手動(dòng)斷開(kāi)這個(gè)Timer侥锦。否則,引用Timer的Controller或者其他類德挣,就會(huì)出現(xiàn)循環(huán)引用而無(wú)法釋放掉恭垦。
比如:
@interface SFViewController ()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation SFViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerEvent) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)timerEvent{
NSLog(@"%s",__func__);
}
- (void)dealloc{
NSLog(@"%s",__func__);
}
@end
假如我Push這樣一個(gè)ViewController,然后pop。
你會(huì)發(fā)現(xiàn)Controller沒(méi)有被釋放格嗅,timer也沒(méi)有被取消番挺。
即使是在dealloc中[self.timer invalidate]也不行。
- (void)dealloc{
NSLog(@"%s",__func__);
[self.timer invalidate];
}
因?yàn)镃ontroller根本沒(méi)有被釋放屯掖,dealloc方法根本不會(huì)調(diào)用玄柏。
如圖,因?yàn)镹STimer內(nèi)部target屬性是強(qiáng)引用贴铜,所以當(dāng)SFViewController強(qiáng)引用NSTimer粪摘,并且將NSTimer的target設(shè)置為SFViewController時(shí),就產(chǎn)生了循環(huán)引用绍坝。
使用NSProxy就可以打破這種循環(huán)徘意。
使用方法:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:[SFProxy proxyWithTarget:self] selector:@selector(timerEvent) userInfo:nil repeats:YES];
2、模擬多繼承
SFProxy:
// .h
@interface SFProxy : NSProxy
-(void)transformToObject:(NSObject *)obj;
@end
// .m
#import "SFProxy.h"
@interface SFProxy ()
@property (nonatomic, strong) NSObject *obj;
@end
@implementation SFProxy
-(void)transformToObject:(NSObject *)obj {
self.obj = obj;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
if (self.obj && [self.obj respondsToSelector:aSelector]) {
return [self.obj methodSignatureForSelector:aSelector];
}
return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
SEL aSelector = [anInvocation selector];
if (self.obj && [self.obj respondsToSelector:aSelector]) {
[anInvocation invokeWithTarget:self.obj];
}
else {
[super forwardInvocation:anInvocation];
}
}
@end
方法調(diào)用:
ClassA *clsA = [[ClassA alloc]init];
ClassB *clsB = [[ClassB alloc]init];
SFProxy *proxy = [SFProxy alloc];
// 變身為clsA的代理
[proxy transformToObject:clsA];
[proxy performSelector:@selector(funcA) withObject:nil];
// 變身為clsB的代理
[proxy transformToObject:clsB];
[proxy performSelector:@selector(funcB) withObject:nil];
打有帧:
2020-07-04 12:44:49.893660+0800 OCTestDemo[71379:1458448] -[ClassA funcA]
2020-07-04 12:44:49.893836+0800 OCTestDemo[71379:1458448] -[ClassB funcB]
三椎咧、NSProxy和NSObject的區(qū)別
雖然NSProxy和class NSObject都定義了-forwardInvocation:
和-methodSignatureForSelector:
,但這兩個(gè)方法并沒(méi)有在protocol NSObject中聲明灾挨;兩者對(duì)這倆方法的調(diào)用邏輯更是完全不同邑退。
對(duì)于class NSObject而言竹宋,接收到消息后先去自身的方法列表里找匹配的selector劳澄,如果找不到,會(huì)沿著繼承體系去superclass的方法列表找蜈七;如果還找不到秒拔,先后會(huì)經(jīng)過(guò)+resolveInstanceMethod:
和-forwardingTargetForSelector:
處理,處理失敗后飒硅,才會(huì)到-methodSignatureForSelector:
/-forwardInvocation:
進(jìn)行最后的掙扎.
但對(duì)于NSProxy砂缩,接收unknown selector后作谚,直接回調(diào)-methodSignatureForSelector:
/-forwardInvocation:
,消息轉(zhuǎn)發(fā)過(guò)程比class NSObject要簡(jiǎn)單得多庵芭。
相對(duì)于class NSObject妹懒,NSProxy的另外一個(gè)非常重要的不同點(diǎn)也值得注意:NSProxy會(huì)將自省相關(guān)的selector直接forward到-forwardInvocation:
回調(diào)中,這些自省方法包括:
- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
- (BOOL)respondsToSelector:(SEL)aSelector;
簡(jiǎn)單來(lái)說(shuō)双吆,這4個(gè)selector的實(shí)際接收者realObject眨唬,而不是NSProxy對(duì)象本身。但另一方面好乐,NSProxy并沒(méi)有將performSelector系列selector也forward到-forwardInvocation:
匾竿,換句話說(shuō),[proxy performSelector:someSelector]
的真正處理者仍然是proxy自身蔚万,只是后續(xù)會(huì)將someSelector給forward到-forwardInvocation
:回調(diào)岭妖,然后經(jīng)由realObject處理。
相關(guān)參考:
NSProxy使用