iOS 避免循環(huán)引用【譯】

今天看文章發(fā)現(xiàn)一片關(guān)于Retain Cycle的老生常談的問題,但是作者從開發(fā)常見場景的代理和Block分析了原因,分析的不錯雹锣,加深了理解网沾,索性小譯一下,加上了一些自己的圖解蕊爵,分享出來辉哥。歡迎轉(zhuǎn)載評論,注明原文地址即可~

Avoid Strong Reference Cycles

隨著ARC的引入攒射,內(nèi)存管理變得更容易了醋旦。然而,即使您不必?fù)?dān)心何時保留和釋放会放,但仍然有一些規(guī)則需要您知道饲齐,以避免內(nèi)存問題。在這篇文章中咧最,我們將討論強引用循環(huán)捂人。

什么是一個強引用循環(huán)?假設(shè)你有兩個對象矢沿,對象A和對象B滥搭。如果對象A于對象B持有強引用,對象B于對象A有強引用捣鲸,那么就形成了一個強引用循環(huán)瑟匆。我們將討論兩種非常常見,需要注意循環(huán)引用的場景:Block和Delegate栽惶。

A->B: strong reference
B->A: strong reference

1. delegate

委托是OC中常用的模式愁溜。在這種情況下疾嗅,一個對象代表另一個對象或與另一個對象協(xié)調(diào)。委派對象保留對另一個對象(委托)的引用冕象,并在適當(dāng)?shù)臅r候向其發(fā)送消息代承。委托可以通過更新應(yīng)用程序的外觀或狀態(tài)來響應(yīng)。

(蘋果的)API的一個典型例子是UITableView及其Delegate交惯。在本例中次泽,UITableView對其Delegate有一個引用,Delegate有一個返回UITableView的引用席爽,按照規(guī)則,每一個都是(指向?qū)Ψ剑┌∑3謱Ψ交钪欢停约词箾]有其他對象指向DelegateUITableView,內(nèi)存也不會被釋放紫谷。(所以需要弱引用)

#import <Foundation/Foundation.h>
 
@class ClassA;

@protocol ClassADelegate <NSObject>
 
-(void)classA:(ClassA *)classAObject didSomething:(NSString *)something;
 
@end
 
@interface ClassA : NSObject
 
@property (nonatomic, strong) id<ClassADelegate> delegate;

這將在ARC世界中生成一個保留循環(huán)齐饮。為了防止這一點,我們需要做的只是將對委托的引用更改為弱引用~

@property (nonatomic, weak) id<ClassADelegate> delegate;
Delegate模式

弱引用并未實現(xiàn)對象間的擁有權(quán)或職責(zé)笤昨,并不能使一個對象存活在內(nèi)存中祖驱。如果沒有其他對象指向delegate代理或者委托對象,那么delegate代理將被釋放瞒窒,隨之delegate代理釋放對委托對象的強引用捺僻。如果沒有其他對象指向委托對象,則委托對象也將被釋放崇裁。

2. Blocks

Block是類似于C函數(shù)的代碼塊匕坯,但除了可執(zhí)行代碼外,它們還可能包含堆棧中的變量拔稳。因此葛峻,Block可以維護一組數(shù)據(jù),用于在執(zhí)行時影響行為采记。因為Block保持代碼的執(zhí)行所需要的數(shù)據(jù),他們是非常有用的回調(diào)挺庞。

官方文檔:

BlockObjective-C對象援制,但是有些內(nèi)存管理規(guī)則只適用于Block褐墅,而非其他Objective-C對象。

Block內(nèi)對任何所捕獲對象的保持強引用,包括Block自身艘款,因此Block很容易引起強引用循環(huán)益眉。如果一個類有這樣一個Block的屬性:

@property (copy) void (^block)(void);

在它的實現(xiàn)中,你有一個這樣的方法:

- (void)methodA {
 
    self.block = ^{
 
        [self methodB];
    };
}
self->block: strong reference
block->self: strong reference

然后你就得到了一個強引用循環(huán):對象selfblock有強引用,而block正好持有一個self的強引用傲诵。

Note: For block properties its a good practice to use copy, because a block needs to be copied to keep track of its captured state outside of the original scope.

注意:關(guān)于block的屬性設(shè)置剧罩,使用copy是一個很好的方式挑势,因為block需要被復(fù)制后用以在原始作用域外來捕獲狀態(tài)。

為了避免這種強引用循環(huán),我們需要再次使用弱引用。下面就是代碼的樣子:

- (void)methodA {
 
    ClassB * __weak weakSelf = self;
 
    self.block = ^{
 
        [weakSelf methodB];
    };
}

通過捕獲對自身的弱引用,block不會保持與對象的強引用。如果對象被釋放之前的block稱為weakself指針將被設(shè)置為nil。雖然這很好,因為不會出現(xiàn)內(nèi)存問題,如果指針為nil禾蚕,那么block內(nèi)的方法就不會被調(diào)用倍试,所以block不會有預(yù)期的行為。為了避免這種情況,我們將進一步修改我們的示例:

- (void)methodA {
 
    __weak ClassB *weakSelf = self;
 
    self.block = ^{
 
        __strong ClassB *strongSelf = weakSelf;
 
        if (strongSelf) {
 
            [strongSelf methodB];
        }
    };
}

我們在block內(nèi)部創(chuàng)建一個Self對象的強引用攘已。此引用將屬于block炮赦,只要block還在,它將存活內(nèi)存中。這不會阻止Self對象被釋放峡眶,我們?nèi)匀豢梢员苊鈴娨醚h(huán)辫樱。

并不是所有的強引用循環(huán)都很容易看到拣展,正如示例中的那樣,當(dāng)您的塊代碼變得更復(fù)雜時敦冬,您可能需要考慮使用弱引用辅搬。

這是兩種常見的模式,它們可以出現(xiàn)強引用循環(huán)夯缺。正如您所看到的蚤氏,只要您能夠正確地識別它們,就很容易用弱引用來破壞這些循環(huán)踊兜。即便ARC讓我們更容易管理內(nèi)存竿滨,但是你仍需要注意。

附注:翻譯中,為了靠近原文意思于游,強引用循環(huán)就是大家經(jīng)常說的循環(huán)引用毁葱。

附:Block的一點碎碎念

  1. block要用copy修飾,還是用strong

NSString贰剥、NSArray倾剿、NSDictionary 等等經(jīng)常使用copy關(guān)鍵字蚌成,是因為他們有對應(yīng)的可變類型:NSMutableString前痘、NSMutableArray、NSMutableDictionary担忧;
block 也經(jīng)常使用 copy 關(guān)鍵字芹缔,具體原因見官方文檔:Objects Use Properties to Keep Track of Blocks:
block 使用 copy 是從 MRC 遺留下來的“傳統(tǒng)”,在 MRC 中,方法內(nèi)部的 block 是在棧區(qū)的,使用 copy 可以把它放到堆區(qū).在 ARC 中寫不寫都行:對于 block 使用 copy 還是 strong 效果是一樣的,但寫上 copy 也無傷大雅瓶盛,還能時刻提醒我們:編譯器自動對 block 進行了 copy 操作最欠。如果不寫 copy ,該類的調(diào)用者有可能會忘記或者根本不知道編譯器會自動對 block 進行了 copy 操作惩猫,他們有可能會在調(diào)用之前自行拷貝屬性值芝硬。這種操作多余而低效。你也許會感覺我這種做法有些怪異轧房,不需要寫依然寫吵取。如果你這樣想,其實是你日用而不知锯厢,

參考

  1. Avoid strong reference cycles
  2. ChenYilong/iOSInterviewQuestions
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市脯倒,隨后出現(xiàn)的幾起案子实辑,更是在濱河造成了極大的恐慌,老刑警劉巖藻丢,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剪撬,死亡現(xiàn)場離奇詭異,居然都是意外死亡悠反,警方通過查閱死者的電腦和手機残黑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來斋否,“玉大人梨水,你說我怎么就攤上這事∫鸪簦” “怎么了疫诽?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我奇徒,道長雏亚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任摩钙,我火速辦了婚禮罢低,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胖笛。我一直安慰自己网持,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布匀钧。 她就那樣靜靜地躺著翎碑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪之斯。 梳的紋絲不亂的頭發(fā)上日杈,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機與錄音佑刷,去河邊找鬼莉擒。 笑死,一個胖子當(dāng)著我的面吹牛瘫絮,可吹牛的內(nèi)容都是我干的涨冀。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼麦萤,長吁一口氣:“原來是場噩夢啊……” “哼鹿鳖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起壮莹,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤翅帜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后命满,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體涝滴,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年胶台,在試婚紗的時候發(fā)現(xiàn)自己被綠了歼疮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡诈唬,死狀恐怖韩脏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情讯榕,我是刑警寧澤骤素,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布匙睹,位于F島的核電站,受9級特大地震影響济竹,放射性物質(zhì)發(fā)生泄漏痕檬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一送浊、第九天 我趴在偏房一處隱蔽的房頂上張望梦谜。 院中可真熱鬧,春花似錦袭景、人聲如沸唁桩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽荒澡。三九已至,卻和暖如春与殃,著一層夾襖步出監(jiān)牢的瞬間单山,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工幅疼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留米奸,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓爽篷,卻偏偏與公主長得像悴晰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逐工,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

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