博客地址:http://istudying.club/,小編自己搭建的博客喲??。痒钝。秉颗。
今天我們來說說循環(huán)引用。什么是循環(huán)引用送矩?當(dāng)兩個不同的對象各有一個強引用指向?qū)Ψ讲仙瑢?dǎo)致雙方的內(nèi)存都無法釋放的時候,那么循環(huán)引用就發(fā)生了栋荸。
不知道大家有沒有聽過一句話:人生就像打電話菇怀,不是你先掛,就是我先掛晌块。其實用到這里是在合適不過了爱沟。假設(shè)有兩個人打電話,彼此都強引用的對方的電話號碼匆背,但是如果雙方都不想掛電話的話呼伸,那么電話就會一直通著。在OC中也是同樣的道理钝尸,正常情況下括享,當(dāng)一個類或者對象即將釋放或者retainCount=0的時候,就會調(diào)用dealloc方法珍促,釋放相應(yīng)的內(nèi)存铃辖。但是,當(dāng)這個對象被其他對象強引用的時候猪叙,那么它就不會被回收娇斩。此時如果處理不當(dāng)?shù)脑捑蜁斐裳h(huán)引用。
首先我們先簡單的看一個循環(huán)引用的例子沐悦。首先我們在第二個控制里面成洗,聲明一個TestThreeController對象three。
@class TestThreeController;
@interface TestSecController : UIViewController
@property (nonatomic, strong) TestThreeController *three;
@end
然后在.m文件中,定義一個按鈕的點擊事件用來push藏否,并重寫dealloc方法瓶殃。
- (void)click
{
_three = [[TestThreeController alloc] init];
_three.sec = self;? // 這就是造成循環(huán)引用的代碼
[self.navigationController pushViewController:_thr animated:YES];
}
- (void)dealloc
{
NSLog(@"TestSecController - dealloc");
}
緊接著我們在第三個控制器里面聲明一個TestSecController對象的sec。
@class TestSecController;
@interface TestThreeController : UIViewController
@property (nonatomic, strong) TestSecController *sec;
@end
然后在其.m文件中重寫dealloc方法副签。
- (void)dealloc
{
NSLog(@"ThreeViewController -? dealloc");
}
正常情況下遥椿,從TestThreeController控制器pop到TestSecController的時候基矮,會執(zhí)行ThreeViewController的dealloc方法。但此時可想而知冠场,不會執(zhí)行家浇,因為循環(huán)應(yīng)用了。其實解決方法很簡單碴裙,就是將中一個屬性修飾符改成weak修飾就好了(關(guān)于strong和weak钢悲,我這里簡單說明一下,一個對象可以有多個指針共同指向舔株,假如A和B兩個Strong指針同時指向一個NSString對象”name”,當(dāng)A重新指向另一個NSString對象”word”時,此時载慈,B仍然指向”name”,如果B也指向別的對象時惭等,因為”name”此時不被任何對象所持有办铡,就會被系統(tǒng)回收辞做。如果B是weak類型的,那么當(dāng)A重指向的時候寡具,因為B是弱引用秤茅,并不持有”name”這個對象,所以”name”就會被系統(tǒng)回收)童叠。
其實OC中代理也是一樣的嫂伞,這里我就拿系統(tǒng)的tableView來舉例,我們在控制器中聲明一個@property (nonatomic, strong) UITableView *tableView的屬性拯钻,那么當(dāng)前控制器就持有了這個tableView,當(dāng)我們在給tableView設(shè)置dataSource和delegate的時候都指向self(也就是當(dāng)前的控制器)撰豺,那么tableView也就持有了當(dāng)前的控制器粪般,所以循環(huán)引用便產(chǎn)生了,現(xiàn)在大家應(yīng)該明白代理為什么用weak或者assign修飾了吧污桦。
現(xiàn)在我們來說一下blcok,相對代理而言亩歹,我個人是比較喜歡blcok的。首先解釋一下凡橱,在聲明block時為啥子用copy?因為block原本在棧區(qū)小作,那么它的生命周期由系統(tǒng)管理,如果它所屬的變量的作用域結(jié)束稼钩,那么它就被廢棄了顾稀。用copy將棧上的block復(fù)制到堆上,這樣它的生命周期就由我們手動進行管理了坝撑,當(dāng)其超過變量作用域時静秆,堆上的block還可以繼續(xù)存在粮揉。
還有一個問題,是不是OC中所有的blcok都需要我們使用copy手動將其復(fù)制到堆上抚笔?答案NO扶认,Cocoa框架中使用含有usingBlock方法名的方法,或者GCD的API中傳遞Block時殊橙,我們不需要這么做辐宾,也就是說在usingBlock和GCD中的block并不會造成循環(huán)引用。所以膨蛮,如果是我們自己聲明的block叠纹,在使用的時候,需要注意會不會造成循環(huán)引用鸽疾,而系統(tǒng)提供的block一般不會造成循環(huán)引用吊洼。
現(xiàn)在我們來說說NSTimer,這里在控制器里面,我們先聲明一個屬性
@property (nonatomic, strong) NSTimer? *timer;
然后初始化timer
_timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(run) userInfo:nil repeats:YES];
NSTimer為什么會造成循環(huán)引用呢制肮,這里我們來分析一下冒窍,首先,控制器持有NSTimer豺鼻,然后在timer初始化的時候综液,target設(shè)置為self,那么timer就持有了當(dāng)前控制器,這就造成了循環(huán)引用儒飒,所以在平時我們不使用定時器的時候谬莹,記得[_timer invalidate];并且_timer = nil;從而避免循環(huán)引用。
說起這個target桩了,我不由得想起了貌似不只有NSTimer有附帽,只有繼承UIControl的都有,于是乎井誉,我又在當(dāng)前控制器聲明了一個UIButton,并添加了addTarget,根據(jù)上面的邏輯蕉扮,這不就造成了循環(huán)引用了么。說實話颗圣,當(dāng)初我一直也想不通喳钟,于是我查閱了一下蘋果的官方文檔。
對于UIButton,說明如下
The control does not retain the object in the target parameter. It is
your responsibility to maintain a strong reference to the target object
while it is attached to a control
大致意思就是button并不會強引用這個target對象在岂,這樣一來奔则,也就不存在循環(huán)引用了。
對于NSTimer,說明如下
The object to which to send the message specified by aSelector when the
timer fires. The timer maintains a strong reference to this object until it (the
timer) is invalidated
大致意思就是timer會強引用這個target對象蔽午,直到timer執(zhí)行了invalidate方法易茬。所以,timer在使用的時候需要注意釋放的問題及老,其次NSTimer繼承NSObject并不是UIControl疾呻,所以不同情況不同對待除嘹。
好了,就說這么多了岸蜗,說的比較簡單尉咕,如果有什么不對的地方,希望留言指正璃岳。