前言
??最近寫代碼時(shí)用到了很多block
适秩,使用不當(dāng)就很容易因?yàn)檠h(huán)引用而造成內(nèi)存泄漏陆蟆。所以在這里簡單分析下什么是block
以及block
循環(huán)引用形成原因以及處理辦法浩嫌,如果有什么說錯的地方钠糊,請大神指出垫言,本文只是想起到一個拋磚迎玉的作用。
??有人可能會問瞻讽,用什么block
啊鸳吸,代理不好嗎,又不會出錯速勇。我在這里要鄭重告訴你們晌砾,為啥用block
,裝逼胺炒拧养匈!裝逼昂哂隆!裝逼芭缓酢;!(重要的事情說三遍)
什么是循環(huán)引用
??循環(huán)引用簡單的說就是兩個對象互相持有對方,所以當(dāng)這兩個對象都不會被釋放猬仁,造成內(nèi)存泄漏帝璧。
舉個??:
對象a創(chuàng)建并引用到了對象b.
對象b創(chuàng)建并引用到了對象c.
對象c創(chuàng)建并引用到了對象b.
??這時(shí)候b和c的引用計(jì)數(shù)分別是2和1。當(dāng)a不再使用b湿刽,調(diào)用release釋放對b的所有權(quán)的烁,因?yàn)閏還引用了b,所以b的引用計(jì)數(shù)為1诈闺,b不會被釋放渴庆。b不釋放,c的引用計(jì)數(shù)就是1买雾,c也不會被釋放。從此杨帽,b和c永遠(yuǎn)留在內(nèi)存中漓穿,造成內(nèi)存浪費(fèi)。
為什么block會造成循環(huán)引用
??我們使用的block
其實(shí)是配置在棧上注盈, block
為了保證代碼塊內(nèi)部對象不被提前釋放晃危,block 會唄復(fù)制到堆上,這樣是我們在寫 block 時(shí)他的屬性是copy
老客。當(dāng)block
被復(fù)制到堆上之后僚饭,block
內(nèi)部對的象會被block
所持有。所以當(dāng)block
內(nèi)部對象又持有 block
時(shí)胧砰,就會造成循環(huán)應(yīng)用鳍鸵。
常見誤區(qū)
1.所有block都會造成循環(huán)引用
??其實(shí)并不是所有的block
都會循造成環(huán)引用,比如UIView
動畫block
尉间、Masonry
添加約束block
偿乖、AFN網(wǎng)絡(luò)請求回調(diào)block
等。
??UIView
動畫block
不會造成循環(huán)引用是因?yàn)檫@是類方法哲嘲,對象不可能強(qiáng)引用一個類贪薪,所以不會造成循環(huán)引用。
??Masonry
約束block
是局部變量眠副,block并沒有持有self画切,超出作用域后,就會被銷毀囱怕,所以也不會造成循環(huán)引用霍弹。
-
Masonry
內(nèi)部代碼
- (NSArray *)mas_makeConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
block(constraintMaker);
return [constraintMaker install];
}
- (NSArray *)mas_updateConstraints:(void(^)(MASConstraintMaker *))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.updateExisting = YES;
block(constraintMaker);
return [constraintMaker install];
}
- (NSArray *)mas_remakeConstraints:(void(^)(MASConstraintMaker *make))block {
self.translatesAutoresizingMaskIntoConstraints = NO;
MASConstraintMaker *constraintMaker = [[MASConstraintMaker alloc] initWithView:self];
constraintMaker.removeExisting = YES;
block(constraintMaker);
return [constraintMaker install];
}
??AFN請求回調(diào)block
不會造成循環(huán)引用是因?yàn)槟銈魅氲?code>block是被AFURLSessionManagerTaskDelegate
對象引用毫别。而AFURLSessionManagerTaskDelegate
被mutableTaskDelegatesKeyedByTaskIdentifier
字典引用,AFN在block
執(zhí)行完后庞萍,mutableTaskDelegatesKeyedByTaskIdentifier
字典會移除AFURLSessionManagerTaskDelegate
對象拧烦,這樣block
也被釋放。所以不存在循環(huán)引用的問題钝计。具體的代碼恋博,請大家去看看 AF 的源碼就知道了。
2.用self 調(diào)用 block 就會造成循環(huán)引用
??并不是所有通過self
調(diào)用帶有block
的方法會引起循環(huán)引用私恬,因?yàn)檠h(huán)引用的就是要雙方互相引用债沮,需要看方法內(nèi)部有沒有持有self
。
舉個??:
[self dismissViewControllerAnimated:YES completion:^{
NSLog(@"%@",self);
}];
??這里乍一看感覺好像循環(huán)引用了本鸣,其實(shí)并沒有疫衩。這里雖然 block
持有對象self
,但是self
并沒有持有 block
荣德,所有 self
和 block
并沒有互相引用闷煤,也就不存在循環(huán)引用了。
3.block中只要不用self就不會造成循環(huán)引用
??在block
中并不只是self
會造成循環(huán)引用涮瞻,用下劃線調(diào)用屬性也會出現(xiàn)循環(huán)引用鲤拿,效果和使用self
是一樣的。
如何避免循環(huán)引用
1.block
的外部對象加上week
修飾
??外部對象加上week
修飾署咽,使用全局弱指針指向一個局部強(qiáng)引用對象近顷,這樣局部變量在超出其作用域后也不會被銷毀。所以不會造成循環(huán)引用宁否。
2.手動將對象置為nil
??將對象置為nil
氛濒。在ARC
中湾揽,被置為nil
的對象會被銷毀。所有這樣就會不會造成 block
和對象相互引用的情況了。但是這種方法不推薦鄙皇,因?yàn)槿绻@個對象存在多個block
的時(shí)候就會出現(xiàn)問題将鸵。
3.使用完之后將block
置為空
??和上一種方法同理爹殊,只是將block
置為 nil
霎槐,這樣 block
就被銷毀了,也不會存在循環(huán)引用了青伤《搅可以在封裝block
的時(shí)候,可以考慮使用完馬上置空當(dāng)前使用的block
狠角,這樣使用的時(shí)候就不需要考慮循環(huán)引用的問題号杠。這個方法很暴力,喜歡暴力美學(xué)的人可以嘗試此方法。
總結(jié)
??使用block
的時(shí)候姨蟋,要避免造成循環(huán)引用屉凯,如果造成循環(huán)引用要知道用哪種方法去修改。不過最好的修改方法就是不用 block