iOS開發(fā)中如何解決強(qiáng)引用循環(huán)

在使用面向?qū)ο蟮木幊陶Z言進(jìn)行開發(fā)的過程中大都涉及到內(nèi)存管理相關(guān)的問題;JAVA、C#等語言采用GC(垃圾回收)機(jī)制來管理內(nèi)存的使用;而最早從事iOS開發(fā)的工程師則經(jīng)歷過MRC(手動(dòng)管理)內(nèi)存的階段,后期apple推出了ARC(自動(dòng)引用計(jì)數(shù))的方式來簡(jiǎn)化內(nèi)存的管理;那么ARC究竟是什么呢?ARC是如何進(jìn)行內(nèi)存管理的呢?

自動(dòng)引用計(jì)數(shù)(ARC)

  • 創(chuàng)建一個(gè)對(duì)象就是在內(nèi)存中開辟了一塊空間來存儲(chǔ)對(duì)象的屬性和行為,對(duì)象都有自己的生命周期,系統(tǒng)如何判斷對(duì)象的生命周期完畢后就對(duì)它進(jìn)行回收呢?
  • iOS系統(tǒng)采用的是引用計(jì)數(shù),在開辟的內(nèi)存區(qū)域中存在一個(gè)NSInteger類型的變量,對(duì)象一旦創(chuàng)建它的值就為1,(通常情況下)有強(qiáng)引用指向它的值就會(huì)+1,強(qiáng)引用置為nil,它的值就會(huì)-1(retain消息會(huì)使得引用計(jì)數(shù)+1,release消息會(huì)使得引用計(jì)數(shù)-1);在一次事件循環(huán)結(jié)束后如果對(duì)象的引用計(jì)數(shù)為0則系統(tǒng)就會(huì)回收該對(duì)象;那么一次的事件循環(huán)還發(fā)生了什么呢?
  • 首先要介紹自動(dòng)釋放池(autoReleasePool):它的實(shí)質(zhì)是一個(gè)NSMutableArray,一次的事件循環(huán)都會(huì)創(chuàng)建一個(gè)自動(dòng)釋放池,事件循環(huán)中產(chǎn)生的對(duì)象會(huì)被依次加入到autoReleasePool中,事件循環(huán)結(jié)束后自動(dòng)釋放池會(huì)一次向存儲(chǔ)的對(duì)象發(fā)送release消息,使得對(duì)象的引用計(jì)數(shù)-1,當(dāng)此操作完畢后,引用計(jì)數(shù)為0的對(duì)象就會(huì)被系統(tǒng)回收了;
  • 總的來說自動(dòng)引用計(jì)數(shù)(ARC)就是iOS系統(tǒng)用來進(jìn)行內(nèi)存管理手段,通過監(jiān)控對(duì)象的引用計(jì)數(shù)值來決定對(duì)象是否應(yīng)該回收;

判斷一個(gè)對(duì)象是否被回收的依據(jù)

對(duì)象的引用計(jì)數(shù)為0時(shí),Objective-C中會(huì)調(diào)用-(void)dealloc而Swift會(huì)調(diào)用deinit {};當(dāng)這兩個(gè)方法被正常調(diào)用時(shí)說明對(duì)象的內(nèi)存管理是正確的;但是也會(huì)出現(xiàn)對(duì)象不被正常釋放的情況,例如:兩個(gè)對(duì)象互相強(qiáng)引用造成循環(huán)引用,使用block或是閉包造成與self的循環(huán)引用等,那么該如何解決這種強(qiáng)引用循環(huán)呢?

循環(huán)強(qiáng)引用

類的實(shí)例之間的循環(huán)強(qiáng)引用
 @interface Person()
 @property(strong)Car* car;
 @end

 @interface Car()
 @property(strong)Person* owner;
 @end   

上述代碼中創(chuàng)建Person和Car的實(shí)例后,為屬性賦值,就會(huì)造成兩者互為強(qiáng)引用,這樣就使得引用計(jì)數(shù)不能為0,ARC就無法對(duì)兩者進(jìn)行內(nèi)存的釋放; 那么該如何打破這種互相強(qiáng)引用呢?Objective-C和Swift都提供了weak關(guān)鍵字的機(jī)制來解決這個(gè)問題;使用weak修飾屬性在賦值的時(shí)候不會(huì)使引用計(jì)數(shù)+1,沒有了強(qiáng)引用那么對(duì)象就能正常釋放!除了weak在Swift中還提供了unowned(無主引用)解決強(qiáng)循環(huán)引用;

Swift中weak和unowned

當(dāng)兩個(gè)實(shí)例出現(xiàn)互相強(qiáng)引用:(1)實(shí)例的值為nil對(duì)邏輯上不造成影響,那么選擇weak(2)實(shí)例的值的一方必須存在,那么只能使用unowned;例如人和信用卡一樣,信用卡的擁有者必須要實(shí)際存在;

block和閉包中出現(xiàn)強(qiáng)循環(huán)引用

#import "HZBlock.h"
typedef void (^TestBlock)(NSString* message);
@interface HZBlock()
@property(nonatomic,copy)TestBlock testBlock;
@property(nonatomic,copy)NSString* name;
@end
@implementation HZBlock

-(void)testBlcok{
    self.testBlock = ^(NSString* message){
       // 循環(huán)引用
        NSString* nameNew = self.name;
        NSLog(@"%@",nameNew);
    };
}   

上述代碼中在block中使用self,編譯器會(huì)報(bào)警告,告知此處會(huì)出現(xiàn)循環(huán)引用(Capturing 'self' strongly in this block is likely to lead to a retain cycle);

在Objective-C中可以weak化self來解決此問題

// weak化self
__weak __typeof(self) weakSelf = self;
// 在block體中為了避免self被釋放,可以再次強(qiáng)引用
__typeof(&*weakSelf) strongSelf = weakSelf;    

在Swift中定義捕獲列表解決閉包內(nèi)的引用循環(huán)

class AutoRefManager: NSObject { 

let name: String
let text: String?

lazy var asHTML: (Void) -> String = {
// 捕獲列表
    [unowned self] in
   self.text!
}

init(name: String, text: String? = nil) {
    self.name = name
    self.text = text
}
}

總結(jié)

在開發(fā)中遇到類似的循環(huán)引用時(shí)要仔細(xì)思考,是否會(huì)造成內(nèi)存泄露的問題,然后再選擇合適的解決方案來解決出現(xiàn)的問題;

最后編輯于
?著作權(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)容