前言
NSProxy是iOS開發(fā)中一個(gè)消息轉(zhuǎn)發(fā)的基類阔馋,它不繼承自NSObject。因?yàn)檫@個(gè)類不太常用苛败,所以對于很多開發(fā)者來說根本沒有用過它缰犁。
不過,這個(gè)類對于iOS開發(fā)還是很有用的捡偏。本文首先會(huì)講解下這個(gè)類的基礎(chǔ)唤冈,然后講解下消息轉(zhuǎn)發(fā)機(jī)制,因?yàn)槲矣X得不懂消息轉(zhuǎn)發(fā)機(jī)制银伟,很難理解更深層次的東西你虹,最后通過一個(gè)實(shí)例來講解下NSProxy的實(shí)際應(yīng)用。
NSProxy
NSProxy本身是一個(gè)抽象類彤避,它遵循NSObject協(xié)議傅物,提供了消息轉(zhuǎn)發(fā)的通用接口。NSProxy通常用來實(shí)現(xiàn)消息轉(zhuǎn)發(fā)機(jī)制和惰性初始化資源琉预。
使用NSProxy董饰,你需要寫一個(gè)子類繼承它,然后需要實(shí)現(xiàn)init以及消息轉(zhuǎn)發(fā)的相關(guān)方法圆米。
//當(dāng)一個(gè)消息轉(zhuǎn)發(fā)的動(dòng)作NSInvocation到來的時(shí)候卒暂,在這里選擇把消息轉(zhuǎn)發(fā)給對應(yīng)的實(shí)際處理對象-(void)forwardInvocation:(NSInvocation*)anInvocation//當(dāng)一個(gè)SEL到來的時(shí)候,在這里返回SEL對應(yīng)的NSMethodSignature -(NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector //是否響應(yīng)一個(gè)SEL +(BOOL)respondsToSelector:(SEL)aSelector
為什么OC中要繼承NSObject而Swift中不用娄帖?
很簡答也祠,Objective C是一個(gè)強(qiáng)烈依賴運(yùn)行時(shí)(Runtime)的一門語言。
你可以利用Runtime來做到很多東西近速,比如方法替換诈嘿,交叉堪旧,屬性添加等等。
但是奖亚,你有沒有想過淳梦,大多數(shù)時(shí)候你并沒有寫任何代碼來支持Runtime,iOS如何知道這些runtime的信息呢昔字?
這就是繼承自NSObject的意義所在
繼承自NSObject之后爆袍,你的所有的類其實(shí)都默認(rèn)實(shí)現(xiàn)了NSObject的許多Runtime相關(guān)方法。
+initialize//在一個(gè)類接收第一條消息之前的+load//在一個(gè)類對象加載到Runtime的時(shí)候調(diào)用
//檢查是否可以向?qū)嵙ο蟀l(fā)送某消息+(BOOL)instancesRespondToSelector:(SEL)aSelector-respondsToSelector:
//向?qū)ο蟀l(fā)送消息-(id)performSelector:(SEL)aSelector-performSelector:withObject:-performSelector:withObject:withObject:...
//動(dòng)態(tài)消息轉(zhuǎn)發(fā)處理機(jī)制+resolveInstanceMethod:-forwardingTargetForSelector:-forwardInvocation:
而Swift不用李滴,Swift中螃宙,非繼承自NSObject的類是靜態(tài)分發(fā)(static dispatch)的,它用的原理是類似C++中的虛表所坯。
簡單來說谆扎,就是在編譯期間,一個(gè)方法的調(diào)用就被編譯成調(diào)用一個(gè)地址了芹助,無法動(dòng)態(tài)修改堂湖。
消息轉(zhuǎn)發(fā)機(jī)制
我們都都知道,對于Objective C來說状土,一個(gè)方法的調(diào)用本質(zhì)上是一個(gè)消息的發(fā)送无蜂。然后Runtime沿著當(dāng)前類的isa,然后逐步的向基類找方法的實(shí)現(xiàn)蒙谓,如果到NSObject還未實(shí)現(xiàn)對應(yīng)的方法斥季,則會(huì)拋出異常。
對于Runtime不熟悉的同學(xué)累驮,可以看看我之前的這篇博客酣倾。
其實(shí),在拋出異常之前谤专,Runtime仍然為我們提供一種機(jī)制躁锡,來處理當(dāng)前對象無法響應(yīng)的方法-消息轉(zhuǎn)發(fā)。
消息轉(zhuǎn)發(fā)涉及到三個(gè)核心方法
//消息轉(zhuǎn)發(fā)第一步置侍,在這里可以動(dòng)態(tài)的為類添加方法映之,這樣類自己就能處理了+resolveInstanceMethod://消息轉(zhuǎn)發(fā)第二步,在第一步無法完成的情況下執(zhí)行蜡坊。這里只是把一個(gè)Selector簡單的轉(zhuǎn)發(fā)給另一個(gè)對象- forwardingTargetForSelector://消息轉(zhuǎn)發(fā)第三步杠输,在第二步也無法完成的情況下執(zhí)行。將整個(gè)消息封裝成NSInvocation秕衙,傳遞下去- forwardInvocation:
消息轉(zhuǎn)發(fā)機(jī)制使得代碼變的很靈活:一個(gè)類本身可以完全不實(shí)現(xiàn)某些方法抬伺,它只要能轉(zhuǎn)發(fā)就可以了。
一個(gè)例子灾梦,破循環(huán)引用
NSTimer是一個(gè)需要添加到Runloop里的類峡钓,對于一個(gè)不會(huì)自動(dòng)停止的Timer,你需要調(diào)用invalidate方法來手動(dòng)斷開這個(gè)Timer若河。否則能岩,引用Timer的Controller或者其他類,就會(huì)出現(xiàn)循環(huán)引用而無法釋放掉萧福。
舉個(gè)例子,在Controller中拉鹃,添加Timer很常見,比如
#import"SecondViewController.h"@interfaceSecondViewController()@property(strong,nonatomic)NSTimer * timer;@end@implementationSecondViewController- (void)viewDidLoad{? ? [superviewDidLoad];self.timer= [NSTimer timerWithTimeInterval:1target:selfselector:@selector(timerInvoked:)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? userInfo:nilrepeats:YES];? ? [[NSRunLoop mainRunLoop] addTimer:self.timerforMode:NSRunLoopCommonModes];}- (void)timerInvoked:(NSTimer *)timer{NSLog(@"1");}- (void)dealloc{NSLog(@"Dealloc");}@end
假如我Push這樣一個(gè)SecondViewController,然后pop鲫忍。
你會(huì)發(fā)現(xiàn)Controller沒有被釋放膏燕,timer也沒有被取消。
我們可以在dealloc中悟民,調(diào)用Timer取消嗎坝辫?比如
- (void)dealloc{? ? [self.timerinvalidate];NSLog(@"Dealloc");}
當(dāng)然不行,因?yàn)镃ontroller根本沒有被釋放射亏,dealloc方法根本不會(huì)調(diào)用近忙。
當(dāng)然,破壞這種循環(huán)引用的方式有很多種智润。本文主要講解如何用NSProxy來破壞及舍。
我們寫一個(gè)WeakProxy來實(shí)現(xiàn)弱引用
@interfaceWeakProxy:NSProxy@property(weak,nonatomic,readonly)idtarget;+ (instancetype)proxyWithTarget:(id)target;- (instancetype)initWithTarget:(id)target;@end@implementationWeakProxy- (instancetype)initWithTarget:(id)target{? ? _target = target;returnself;}+ (instancetype)proxyWithTarget:(id)target{return[[selfalloc] initWithTarget:target];}- (void)forwardInvocation:(NSInvocation *)invocation{? ? SEL sel = [invocation selector];if([self.targetrespondsToSelector:sel]) {? ? ? ? [invocation invokeWithTarget:self.target];? ? }}- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{return[self.targetmethodSignatureForSelector:aSelector];}- (BOOL)respondsToSelector:(SEL)aSelector{return[self.targetrespondsToSelector:aSelector];}@end
然后,這樣創(chuàng)建Timer窟绷,
self.timer= [NSTimer timerWithTimeInterval:1target:[WeakProxy proxyWithTarget:self]? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? selector:@selector(timerInvoked:)? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? userInfo:nilrepeats:YES];
你會(huì)發(fā)現(xiàn)可以釋放了锯玛。
原理如下: