- 首先在我們的程序中添加一段循環(huán)引用代碼
NSMutableArray * arr1 = [NSMutableArray array];
NSMutableArray * arr2 = [NSMutableArray array];
[arr1 addObject:arr2];
[arr2 addObject:arr1];
-
選擇
Product->Profileimage -
選擇
Leaks,點(diǎn)擊Chooseimage -
分析結(jié)果
image
點(diǎn)擊左上角1位置的紅點(diǎn)開(kāi)始運(yùn)行;
我們會(huì)發(fā)現(xiàn)在2的位置上出現(xiàn)一個(gè)錯(cuò)誤警告;
選中3Leak Checks;
點(diǎn)擊4位置,選擇Cycles&Roots;
我們會(huì)看到5的位置上有一條信息,描述是簡(jiǎn)單循環(huán);
選中那條信息,我們可以看到6位置上呈現(xiàn)出循環(huán)引用的圖示,是不是一下就清晰明了了。
我們?cè)賮?lái)看看多個(gè)對(duì)象之間的循環(huán)引用是什么樣子的
NSMutableArray * array1 = [NSMutableArray array];
NSMutableArray * array2 = [NSMutableArray array];
NSMutableArray * array3 = [NSMutableArray array];
NSMutableArray * array4 = [NSMutableArray array];
[array1 addObject:array2];
[array2 addObject:array3];
[array3 addObject:array4];
[array4 addObject:array1];

通過(guò)這樣的方式來(lái)監(jiān)測(cè)循環(huán)引用是不是變得很容易方便,也很清晰明了呢
關(guān)于循環(huán)引用
循環(huán)引用的實(shí)質(zhì)是,多個(gè)對(duì)象之間相互強(qiáng)引用,導(dǎo)致不能釋放,讓系統(tǒng)回收。iOS開(kāi)發(fā)中常見(jiàn)的循環(huán)引用主要是由Delegate、NSTimer和block引起。
一、代理(Delegate)
delegate是開(kāi)發(fā)中比較常見(jiàn)到的循環(huán)引用,一般在聲明delegate的時(shí)候,都需要使用弱引用weak或者assign。MRC下,只能用assign,ARC下,最好用weak,因?yàn)閣eak修飾的變量在釋放后,會(huì)自動(dòng)指向nil,防止出現(xiàn)野指針。
二、定時(shí)器(NSTimer)
在控制器(Controller)內(nèi),創(chuàng)建NSTimer實(shí)例作為其屬性,由于定時(shí)器創(chuàng)建后,也會(huì)強(qiáng)引用該控制器對(duì)象,那么該控制器對(duì)象和定時(shí)器對(duì)象就相互循環(huán)引用了。
要解決該循環(huán)引用,可以手動(dòng)斷開(kāi):
如果是不重復(fù)的NSTimer對(duì)象,在回調(diào)方法里將其invalidate并置為nil即可。
如果是重復(fù)的NSTimer對(duì)象,在適當(dāng)位置將其invalidate并置為nil即可。
三、block
示例:
@property (nonatomic, copy) dispatch_block_t testBlock;
@property (nonatomic, copy) NSString *testString;
- (void)test {
self.testBlock = ^() {
NSLog(@"%@", self.testString);
};
}
此時(shí)編譯器如出現(xiàn)循環(huán)引用的警告??"Capturing 'self' strongly in this block is likely to lead to a retain cycle"。由于block會(huì)對(duì)block內(nèi)的對(duì)象進(jìn)行持有操作,就相當(dāng)于持有了其中的對(duì)象,而如果此時(shí)block中的對(duì)象又持有了該block,則會(huì)造成循環(huán)引用。
解決方法就是使用__weak修飾self:
- (void)test {
__weak typeof(self) weakSelf = self;
self.testBlock = ^() {
NSLog(@"%@", weakSelf.testString);
};
}
并不是所有的block都會(huì)造成循環(huán)引用,只有被強(qiáng)引用了的block才會(huì)產(chǎn)生循環(huán)引用,例如:dispatch_async(dispatch_get_main_queue(), ^{})、[UIView animateWithDuration:1 animations:^{}]這些系統(tǒng)方法等,或者block并不是其屬性而是臨時(shí)變量,即棧block。
還有一種場(chǎng)景,在block執(zhí)行開(kāi)始時(shí),self對(duì)象還未被釋放,而執(zhí)行過(guò)程中,self被釋放了,由于是用weak修飾的,那么weakSelf也被釋放了,此時(shí)在block內(nèi)訪問(wèn)weakSelf,就可能會(huì)發(fā)生錯(cuò)誤(向nil對(duì)象發(fā)消息并不會(huì)崩潰,但是沒(méi)有效果)。
對(duì)于這種場(chǎng)景,應(yīng)該再block中對(duì)對(duì)象使用__strong修飾,使得在block期間對(duì)對(duì)象持有,block執(zhí)行結(jié)束后,解除其持有。示例如下:
__weak typeof(self) weakSelf = self;
self.testBlock = ^() {
__strong __typeof(self) strongSelf = weakSelf;
[strongSelf testMethod];
};


