Block 代碼塊中防止self循環(huán)引用的問題

一、什么是循環(huán)引用

循環(huán)引用指兩個(gè)對(duì)象相互強(qiáng)引用了對(duì)方,即retain了對(duì)方,從而導(dǎo)致誰也釋放不了誰的內(nèi)存泄露問題。如聲明一個(gè)delegate一般用assign而不能用retainstrong,因?yàn)槟阋坏┠敲醋隽耍艽罂赡芤鹧h(huán)引用。在以往的項(xiàng)目中,我?guī)状斡脛?dòng)態(tài)內(nèi)存檢查發(fā)現(xiàn)了循環(huán)引用導(dǎo)致的內(nèi)存泄露。

二、block中的循環(huán)引用

這里講的是block的循環(huán)引用問題,因?yàn)?strong>block在拷貝到堆上的時(shí)候(為什么要拷貝到堆上?見下面補(bǔ)充),會(huì)retain其引用的外部變量,那么如果block中如果引用了他的宿主對(duì)象,那很有可能引起循環(huán)引用,如:

- (void)dealloc
{
NSLog(@"no cycle retain");
}
- (id)init
{
self = [super init];
if (self) {

#if TestCycleRetainCase1
    //會(huì)循環(huán)引用
    self.myblock = ^{

        [self doSomething];
    };
#elif TestCycleRetainCase2

    //會(huì)循環(huán)引用
    __block TestCycleRetain *weakSelf = self;
    self.myblock = ^{

        [weakSelf doSomething];
    };

#elif TestCycleRetainCase3

    //不會(huì)循環(huán)引用
    __weak TestCycleRetain *weakSelf = self;
    self.myblock = ^{

        [weakSelf doSomething];
    };

#elif TestCycleRetainCase4

    //不會(huì)循環(huán)引用
    __unsafe_unretained TestCycleRetain *weakSelf = self;
    self.myblock = ^{

        [weakSelf doSomething];
    };

#endif
    NSLog(@"myblock is %@", self.myblock);
}
return self;
}
- (void)doSomething
{
NSLog(@"do Something");
}
int main(int argc, char *argv[]) {
    @autoreleasepool {
    TestCycleRetain* obj = [[TestCycleRetain alloc] init];
    obj = nil;
    return 0;
    }
}

經(jīng)過上面的ARC環(huán)境測(cè)試發(fā)現(xiàn),在加了__weak__unsafe_unretained的變量引入后,TestCycleRetain方法可以正常執(zhí)行dealloc方法,而不轉(zhuǎn)換和用__block轉(zhuǎn)換的變量都會(huì)引起循環(huán)引用。
但是實(shí)際情況是:
1)MRC情況下,用__block可以消除循環(huán)引用。
2)ARC情況下,必須用弱引用才可以解決循環(huán)引用問題,iOS 5之后可以直接使用__weak,之前則只能使用__unsafe_unretained了,__unsafe_unretained缺點(diǎn)是指針釋放后自己不會(huì)置空。

示例代碼:(關(guān)于使用block會(huì)防止循環(huán)引用的代碼示例)
1)在ARC下,由于__block抓取的變量一樣會(huì)被Block retain,所以必須用弱引用才可以解決循環(huán)引用問題,iOS 5之后可以直接使用__weak,之前則只能使用__unsafe_unretained__unsafe_unretained缺點(diǎn)是指針釋放后自己不會(huì)置空。示例代碼:

//iOS 5之前可以用__unsafe_unretained

//__unsafe_unretained typeof(self) weakSelf = self;

__weak typeof(self) weakSelf = self;

self.myBlock = ^(int paramInt)

{

//使用weakSelf訪問self成員

[weakSelf anotherFunc];

};
2)在非ARC下,顯然無法使用弱引用,這里就可以直接使用__block來修飾變量,它不會(huì)被Block所retain的,參考代碼:

//非ARC

__block typeof(self) weakSelf = self;

self.myBlock = ^(int paramInt)

{

//使用weakSelf訪問self成員

[weakSelf anotherFunc];

};

三、補(bǔ)充:為甚么要將block要拷貝到堆上?

一般來說我們總會(huì)在設(shè)置Block之后,在合適的時(shí)間回調(diào)Block,而不希望回調(diào)Block的時(shí)候Block已經(jīng)被釋放了,所以我們需要對(duì)Block進(jìn)行copy,copy到堆中,以便后用。
當(dāng)一個(gè)BlockCopy的時(shí)候,如果你在Block里進(jìn)行了一些調(diào)用,那么將會(huì)有一個(gè)強(qiáng)引用指向這些調(diào)用方法的調(diào)用者(也就是上面講的,會(huì)存在循環(huán)引用導(dǎo)致內(nèi)存泄露的風(fēng)險(xiǎn)),其有兩個(gè)規(guī)則:
如果你是通過引用來訪問一個(gè)實(shí)例變量,那么將強(qiáng)引用至self
如果你是通過值來訪問一個(gè)實(shí)例變量,那么將直接強(qiáng)引用至這個(gè)“值”變量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容