iOS 中的循環(huán)引用

此處記錄的都是ARC情況下~~~

循環(huán)引用是什么抛猖?

當(dāng)兩個(gè)不同的對(duì)象各有一個(gè)強(qiáng)引用指向?qū)Ψ浇冢敲囱h(huán)引用便產(chǎn)生了稻爬,當(dāng)然多一個(gè)對(duì)象產(chǎn)生的環(huán)也是一樣的。

兩個(gè)對(duì)象通過(guò)彼此之間的強(qiáng)引用構(gòu)成保留環(huán)

也可以直接從引用計(jì)數(shù)說(shuō)明矛辕,此時(shí)如果互相引用的時(shí)候,雙方的引用計(jì)數(shù)都是+1的漓柑,導(dǎo)致任何時(shí)候引用計(jì)數(shù)都不為0竟宋,始終無(wú)法釋放,無(wú)法釋放他們的內(nèi)存粘捎,即使已經(jīng)沒(méi)有變量持有他們薇缅。

循環(huán)引用帶來(lái)什么危害

循環(huán)引用對(duì) app 有潛在的危害危彩,會(huì)使內(nèi)存消耗過(guò)高,性能變差和 app 閃退等捅暴。

循環(huán)引用有哪些具體的情況恬砂?block ? delegate ? NSTimer?

1蓬痒、父類(lèi)與子類(lèi)

例如泻骤,我們?cè)谑褂肬ITableView 的時(shí)候,傳一個(gè) Controller 給 Cell 使用

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    TestTableViewCell *cell =[tableView dequeueReusableCellWithIdentifier:@"UITableViewCellIden" forIndexPath:indexPath];
    cell.tableView = tableView;
    return cell;
}
@interface TestTableViewCell : UITableViewCell
@property (nonatomic, weak) UITableView *tableView;
@end

此時(shí)如果我們給 tableView 的屬性特質(zhì)strong就會(huì)用造成循環(huán)引用的梧奢,UITableView ==> UITableViewCell; UITableViewCell ==> UITableView 狱掂。因此 weak是肯定的。

2亲轨、Block

Block 是我們常用到的趋惨,也是我們最要注意循環(huán)引用的。在此惦蚊,先拿一個(gè)我們使用 Block 的例子:

typedef void (^TestCircleBlock)();
@property (nonatomic, copy) TestCircleBlock testCircleBlock;
if (self.testCircleBlock) {
    // 具體實(shí)現(xiàn)
    self.testCircleBlock();
}
  • blockcopy時(shí)都會(huì)對(duì)block內(nèi)部用到的對(duì)象進(jìn)行強(qiáng)引用的器虾。
  • 該類(lèi)又將block作為自己的屬性變量,而該類(lèi)在block的方法體里面又使用了該類(lèi)本身蹦锋,此時(shí)就很簡(jiǎn)單的形成了一個(gè)環(huán)啦兆沙。
self.testObject.testCircleBlock = ^{
   [self doSomething];
};

進(jìn)行弱引用后,打破了循環(huán)莉掂,我們可以這樣:

 __weak typeof(self) weakSelf = self;
 self.testObject.testCircleBlock = ^{
      __strong typeof (weakSelf) strongSelf = weakSelf;
      [strongSelf doSomething];
};

在 ARC 中葛圃,在被拷貝的 block 中無(wú)論是直接引用 self 還是通過(guò)引用 self 的成員變量間接引用 self,該 block 都會(huì) retain self憎妙。
但是注意 block 里面直接用“成員變量”的情況库正,有些情況下我們是沒(méi)法直接撲捉到 間接引用的那個(gè)self,或者說(shuō)撲捉到的那個(gè)self 是不對(duì)的,也就沒(méi)法對(duì)其進(jìn)行weak引用啦厘唾,循環(huán)依然存在褥符,所以建議直接用屬性!

總的說(shuō)來(lái)抚垃,block 我們很有必要深入了解喷楣,了解為什么用 copy,了解它在內(nèi)存中的位置,更有利于我們理解的讯柔。Block基礎(chǔ)和retain cycle(循環(huán)引用)

3抡蛙、Delegate

相信類(lèi)似下面這樣的代理屬性,我們寫(xiě)了不要太多遍了吧

@property (nonatomic, weak) id <TestDelegate> delegate;

說(shuō)白了就是循環(huán)使用的問(wèn)題魂迄,假如我們是寫(xiě)的strong,那么 兩個(gè)類(lèi)之間調(diào)用代理就是這樣的啦

BViewController *bViewController = [[BViewController alloc] init];
bViewController.delegate = self; //假設(shè) self 是AViewController
[self.navigationController pushViewController:bViewController animated:YES];

/**
 假如是 strong 的情況
    bViewController.delegate ===> AViewController (也就是 A 的引用計(jì)數(shù) + 1)
    AViewController 本身又是引用了 <BViewControllerDelegate> ===> delegate 引用計(jì)數(shù) + 1
 導(dǎo)致: AViewController <======> Delegate 粗截,也就循環(huán)引用啦
 */
  • Delegate創(chuàng)建并強(qiáng)引用了 AViewController;(strong ==> A 強(qiáng)引用捣炬、weak ==> 引用計(jì)數(shù)不變)

所以用 strong的情況下熊昌,相當(dāng)于 Delegate 和 A 兩個(gè)互相引用啦绽榛,A 永遠(yuǎn)會(huì)有一個(gè)引用計(jì)數(shù) 1 不會(huì)被釋放,所以造成了永遠(yuǎn)不能被內(nèi)存釋放婿屹,因此weak是必須的灭美。

4、NSTimer

NSTimer 其實(shí)相對(duì)來(lái)說(shuō)昂利,我們其實(shí)是很容易忽略它這種情況的届腐,畢竟還是很特殊的。

循環(huán)引用

此時(shí)解決的方法還是用 “Effective Objective-C ”中的52條方法

#import <Foundation/Foundation.h>

@interface NSTimer (YPQBlocksSupport)

+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats;

@end


#import "NSTimer+YPQBlocksSupport.h"

@implementation NSTimer (YPQBlocksSupport)


+ (NSTimer *)ypq_scheduledTimeWithTimeInterval:(NSTimeInterval)interval
                                         block:(void(^)())block
                                       repeats:(BOOL)repeats
{
    return [self scheduledTimerWithTimeInterval:interval
                                         target:self
                                       selector:@selector(ypq_blockInvoke:) userInfo:[block copy]
                                        repeats:repeats];
}

- (void)ypq_blockInvoke:(NSTimer *)timer
{
    void (^block)() = timer.userInfo;
    if(block)
    {
        block();
    }
}

@end

具體使用

__weak ViewController * weakSelf = self;
[NSTimer ypq_scheduledTimeWithTimeInterval:4.0f
                                     block:^{
                                         ViewController * strongSelf = weakSelf;
                                         [strongSelf afterThreeSecondBeginAction];
                                     }
                                   repeats:YES];

計(jì)時(shí)器保留其目標(biāo)對(duì)象蜂奸,反復(fù)執(zhí)行任務(wù)導(dǎo)致的循環(huán)犁苏,確實(shí)要注意,另外在dealloc的時(shí)候扩所,不要忘了調(diào)用計(jì)時(shí)器中的 invalidate方法围详。

如何避免循環(huán)引用?理解上面幾種情況的發(fā)生的情況祖屏,我們一般就不會(huì)造成循環(huán)引用啦助赞,反正永遠(yuǎn)遵循,不要讓對(duì)象不能被釋放袁勺,誰(shuí)創(chuàng)建誰(shuí)釋放雹食!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市魁兼,隨后出現(xiàn)的幾起案子婉徘,更是在濱河造成了極大的恐慌漠嵌,老刑警劉巖咐汞,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異儒鹿,居然都是意外死亡化撕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)约炎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)植阴,“玉大人,你說(shuō)我怎么就攤上這事圾浅÷邮郑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵狸捕,是天一觀(guān)的道長(zhǎng)喷鸽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)灸拍,這世上最難降的妖魔是什么做祝? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任砾省,我火速辦了婚禮,結(jié)果婚禮上混槐,老公的妹妹穿的比我還像新娘编兄。我一直安慰自己,他們只是感情好声登,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布狠鸳。 她就那樣靜靜地躺著,像睡著了一般悯嗓。 火紅的嫁衣襯著肌膚如雪碰煌。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天绅作,我揣著相機(jī)與錄音芦圾,去河邊找鬼。 笑死俄认,一個(gè)胖子當(dāng)著我的面吹牛个少,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眯杏,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼夜焦,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了岂贩?” 一聲冷哼從身側(cè)響起茫经,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎萎津,沒(méi)想到半個(gè)月后卸伞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锉屈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年荤傲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片颈渊。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡遂黍,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出俊嗽,到底是詐尸還是另有隱情雾家,我是刑警寧澤,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布绍豁,位于F島的核電站芯咧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唬党,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一鹃共、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驶拱,春花似錦霜浴、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至税迷,卻和暖如春永丝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背箭养。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工慕嚷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毕泌。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓喝检,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親撼泛。 傳聞我的和親對(duì)象是個(gè)殘疾皇子挠说,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容