iOS的內(nèi)存管理(1) 一些概念點

前言

通過這段時間的學習沧奴,對Objective-C的內(nèi)存管理知識做一個總結奕污。分享給大家,如有理解錯誤的地方吞鸭,還望多指正寺董。
總結從以下幾個方面來說明:

  1. 引用計數(shù)器
  2. ARC(Automatic Reference Counting):自動引用計數(shù)
  3. 循環(huán)引用問題
  4. 自動釋放池autorelease pool

正文

1 引用計數(shù)器

1.理解引用計數(shù)器
說到引用計數(shù)器,有著iOS開發(fā)經(jīng)驗的同行一定知道刻剥,Objective-C語言內(nèi)存管理的核心就是引用計數(shù)器遮咖。
簡單來說,每個OC對象都有一個引用計數(shù)器造虏,如果想使某個對象繼續(xù)存在內(nèi)存中御吞,那就使其引用計數(shù)增加1,如果該對象使用結束漓藕,我們不希望它繼續(xù)存在于內(nèi)存中陶珠,那就使其引用計數(shù)減少1(,當該對象的引用計數(shù)等于0時候享钞,系統(tǒng)收回該對象的內(nèi)存揍诽。

  1. 引用計數(shù)器的工作原理
    NSObject協(xié)議下聲明了一下三個方法用于操作引用計數(shù)器:
  • retain 遞增引用計數(shù);
  • release 遞減引用計數(shù)栗竖;
  • autorelease 待稍后清理引用釋放池時暑脆,再遞減引用計數(shù)。
    查看當前引用計數(shù)的方法是retainCount狐肢,[obj retainCount]添吗。

對象創(chuàng)建出來的時候,其引用計數(shù)至少為1份名。若想讓它繼續(xù)存活碟联,則調用retain方法,若該對象不再被使用僵腺,則調用release或者autorelease方法鲤孵。當引用計數(shù)為0時候,該對象占用的內(nèi)存將會被回收辰如。引用計數(shù)器的工作原理大概如此普监。

2 ARC(Automatic Reference Counting):自動引用計數(shù)
  1. 自動引用計數(shù)(ARC)的理解
    在ARC出現(xiàn)前,開發(fā)者使用引用計數(shù)需要記住何時使用retainreleaseautorelease鹰椒,而ARC的誕生就是為了解決這個問題。ARC省去了開發(fā)者在代碼中調用retain呕童、releaseautorelease精力漆际,取代了開發(fā)者內(nèi)存管理的工作。
  2. ARC的工作原理
    Xcode的Clang編譯器帶有一個靜態(tài)分析器夺饲,用于檢測程序中引用計數(shù)有問題的地方奸汇。例如:
if (true) {
   id obj = [[SomeClass alloc] init];
   [obj doSometing];
}

這段代碼在MRC環(huán)境下就會出問題,因為if條件外obj沒有被釋放娶靡,此處會發(fā)生內(nèi)存泄漏璧微。靜態(tài)分析器做的就是檢測這樣的錯誤奈应。
既然靜態(tài)分析器可以做到這些,那么可以應用這個功能贯涎,提前在程序中加入retainrelease等操作慢洋。ARC的工作原理塘雳,就是使用了這一功能。
在ARC下普筹,代碼經(jīng)過編譯后败明,會自動為源代碼添加上相對應的操作。所以在ARC下太防,retain,release,autorelease都是不允許被使用的妻顶,否則會產(chǎn)生編譯錯誤。

3.ARC的一些tips

  • ARC在調用retain,release,autorelease方法的時候蜒车,并不走Objctive-C的消息派發(fā)機制讳嘱,而是直接調用底層的C語言方法。這樣做提升了性能醇王,也是retain,release,autorelease方法不能被重寫的原因呢燥。
  • OC語法有非常嚴格的命名規(guī)則,以下列單詞開頭的方法名:new alloc copy mutableCopy寓娩。若方法返回對象叛氨,則ARC不會為返回的對象加上autorelease,否則會在返回對象前為其加上autorelease
  • 除了自動的調用retain,release,autorelease之外棘伴,ARC還能夠把互相抵消的retain,release,autorelease操作簡化寞埠。若某個對象上重復多次的進行了‘retain’和‘release’操作,那么ARC有時可以成對的抵消這兩個操作焊夸。
  • ARC也包含運行期組件仁连。當它檢測到某方法返回對象前,為其執(zhí)行了autorelease操作,之后該對象還要執(zhí)行retain操作饭冬,那ARC就會刪除這一對操作使鹅。具體方式為,在返回對象前昌抠,不直接調用autorelease方法患朱,而是調用objc_autoreleaseReturnValue函數(shù),此函數(shù)會檢測當前方法返回之后即將要執(zhí)行的代碼炊苫,若發(fā)現(xiàn)那段代碼要在返回對象上執(zhí)行retain操作裁厅,則設立一個flag不再執(zhí)行原有的autorelease操作。同理侨艾,若方法返回一個自動釋放的對象执虹,而該對象需要被保留,那么不直接執(zhí)行retain唠梨,而是改為執(zhí)行objc_retainAutoreleasedReturnValue函數(shù)袋励,此函數(shù)檢測剛才設立的那個flag,若已經(jīng)設置姻成,則不執(zhí)行retain操作插龄。
    objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue兩個函數(shù)的實現(xiàn)必須通過查看機器碼指令才可以判斷,所以是由編譯器的開發(fā)者完成的科展。
  • 用以下修飾符修飾變量時的一些語義:
    __strong:默認語義均牢,保留此值;
    __unsafe_unretained:不保留此值才睹;
    __weak:不保留此值徘跪,但是變量可以安全使用,如果系統(tǒng)回收了該對象琅攘,那么這個變量也會被自動清空垮庐;
    __autoreleasing:在方法調用時,使用這個修飾參數(shù)值坞琴,在方法返回后哨查,該值自動釋放。
  • ARC環(huán)境下剧辐,當對象被回收時寒亥,實例變量的回收問題:ARC會使用Objctive-C++的一項特性來清理實例變量∮兀回收Objective-C++對象時溉奕,待回收對象會調用所有C++對象的析構函數(shù)。編譯器如果發(fā)現(xiàn)某個對象有C++對象忍啤,就會生成名為.cxx_destruct的方法加勤。ARC借助此特性,在該方法中生成清理內(nèi)存所需要的代碼。
  • CoreFoundation對象需要手動管理內(nèi)存鳄梅,不歸ARC管理叠国,開發(fā)者必須自己調動CFRetain/CFRelease
3 循環(huán)引用問題
  1. 循環(huán)引用的理解
    如果A對象強引用了B對象戴尸,而B對象也強引用了A對象煎饼,這就是最簡單的循環(huán)引用,兩個對象間的互相引用校赤。當系統(tǒng)要回收對象A時,由于A引用了對象B筒溃,所以對象B也需要被釋放马篮,而此時B又強引用了A,如此一來怜奖,兩個對象都不能夠釋放浑测,繼續(xù)存活于內(nèi)存中,就會出現(xiàn)內(nèi)存泄漏歪玲。這就是循環(huán)引用的問題迁央。
  2. 循環(huán)引用問題的解決
    解決循環(huán)引用問題的最佳方式就是 弱引用。用unsafe_unretained或者weak修飾屬性滥崩。在語義上unsafe_unretainedassign等價岖圈,區(qū)別于assign用于修飾通用類型的屬性,比如int,float結構體等钙皮,而unsafe_unretained用于修飾對象蜂科。
    例如對象A強引用了對象B,那么對象B如果弱引用了對象A短条,就不會出現(xiàn)以上的問題导匣。當系統(tǒng)回收對象A時,對象B會被回收茸时,而B對A的弱引用不會造成循環(huán)引用贡定,所以不會出現(xiàn)內(nèi)存泄漏的問題。
    weak等價于unsafe_unretained可都,它們的不同主要表現(xiàn)在被修飾的屬性被釋放后的行為不同缓待。當用unsafe_unretained修飾的屬性被回收后,該屬性任然指向那個被回收的屬性汹粤,而weak則指向nil命斧。使用weak會使程序更加安全一些。
  3. block使用中的循環(huán)引用問題
    這個問題在很多的技術文章中被提到過嘱兼,這里也做個簡單的說明国葬。
    例如:
//DemoViewController.m
@interface DemoViewController ()
@property (nonatomic, copy) void (^testBlock) (void);
@end

@implementation DemoViewController
...
- (void)viewDidLoad {
  [super viewDidLoad];
  [self test];
}

- (void)test {
  self.testBlock = ^(){
    [self doSometing];
  }
}
...

@end

以上這段代碼,由于testBlock塊中捕獲了self,所以testBlock強引用了self,而同時self強引用著testBlock,如此就形成了循環(huán)引用汇四,有內(nèi)存泄漏的風險接奈。
打破這種保留的方式很簡單,使用__weak定義一個新的weakSelftestBlock捕獲通孽。如下:

//DemoViewController.m

@interface DemoViewController ()
@property (nonatomic, copy) void (^testBlock) (void);
@end

@implementation DemoViewController
...
- (void)viewDidLoad {
  [super viewDidLoad];
  [self test];
}

- (void)test {
  __weak typeof(self) weakSelf = self;
  self.testBlock = ^(){
    [weakSelf doSometing];
  }
}
...
@end

關于block的知識點總結序宦,會在后面整理一份。

4 自動釋放池autorelease pool
  1. 自動釋放池的認識
    在ARC中背苦,自動釋放池(autorelease pool)是一項重要的特性互捌。
    當某個對象調用release時,會理解遞減引用計數(shù)retainCount行剂。如果換做調用autorelease,則對象會被加入autorelease pool中秕噪,當清空自動釋放池autorelease pool時,會向其中的對象發(fā)送release消息厚宰。
  2. 自動釋放池的使用
    使用自動釋放池的語法如下:
//使用語法
@autoreleasepool{
  //...
}

一般情況下腌巾,系統(tǒng)創(chuàng)建的主線程或者GCD機制中的線程,都會默認創(chuàng)建自己的自動釋放池铲觉,每次執(zhí)行 事件循環(huán) 時澈蝙,就會將其清空。因此撵幽,不需要自己來創(chuàng)建 自動釋放池塊灯荧。
應用程序的入口int main()函數(shù)處,就為我們手動創(chuàng)建了應用程序的自動釋放池盐杂。

int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

所以通常情況下我們無需自己創(chuàng)建自動釋放池漏麦。當某些臨時產(chǎn)生的對象導致應用程序的內(nèi)存峰值過高時,我們可以通過創(chuàng)建自動釋放池况褪,來解決這個問題撕贞。
例如:

NSMutableArray *objsArray = [NSMutableArray array];
for (int i = 0; i < 10000; i ++) {
  id obj = [self createSomeObjcWithi:i];
  [objsArray addObject:obj];
}

以上代碼就會造成程序的內(nèi)存突然暴增,而等所有obj對象都釋放以后测垛,又突然下降捏膨。
此時,增加一個自動釋放池代碼塊即可解決這個問題:

NSMutableArray *objsArray = [NSMutableArray array];
for (int i = 0; i < 10000; i ++) {
  @atuoreleasepool {
    id obj = [self createSomeObjcWithi:i];
    [objsArray addObject:obj];
  }
}

這樣一來食侮,應用程序在執(zhí)行循環(huán)時号涯,就會有效降低內(nèi)存峰值,不像原來那么高锯七。
創(chuàng)建自動釋放池本身也會占用一定的內(nèi)存链快,所以是否使用自動釋放池完全取決于程序本身。

關于自動釋放池Draveness大神的自動釋放池的前世今生 ---- 深入解析 Autoreleasepool有詳細的解析眉尸。

總結

內(nèi)存管理是應用程序的靈魂域蜗,雖然在ARC環(huán)境下巨双,我們可以盡量少的投入精力在內(nèi)存管理上,但是了解其中的原理和機制霉祸,會讓我們在程序出問題時找到有效的解決途徑筑累,更是提高自我的一種方式。
當然丝蹭,內(nèi)存管理涉及到的也不止文中提到的內(nèi)容慢宗,還有很多需要挖掘的地方。
文章是本人看書學習中的總結奔穿,主要用于知識鞏固镜沽,順便和大家分享交流,如果有不妥的地方贱田,歡迎指正淘邻。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市湘换,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌统阿,老刑警劉巖彩倚,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異扶平,居然都是意外死亡帆离,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門结澄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哥谷,“玉大人,你說我怎么就攤上這事麻献∶峭祝” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵勉吻,是天一觀的道長监婶。 經(jīng)常有香客問我,道長齿桃,這世上最難降的妖魔是什么惑惶? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮短纵,結果婚禮上带污,老公的妹妹穿的比我還像新娘。我一直安慰自己香到,他們只是感情好鱼冀,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布报破。 她就那樣靜靜地躺著,像睡著了一般雷绢。 火紅的嫁衣襯著肌膚如雪泛烙。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天翘紊,我揣著相機與錄音蔽氨,去河邊找鬼。 笑死帆疟,一個胖子當著我的面吹牛鹉究,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踪宠,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼自赔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了柳琢?” 一聲冷哼從身側響起绍妨,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎柬脸,沒想到半個月后他去,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡倒堕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年灾测,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垦巴。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡媳搪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骤宣,到底是詐尸還是另有隱情秦爆,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布憔披,位于F島的核電站鲜结,受9級特大地震影響,放射性物質發(fā)生泄漏活逆。R本人自食惡果不足惜精刷,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蔗候。 院中可真熱鬧怒允,春花似錦、人聲如沸锈遥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至丽惶,卻和暖如春炫七,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钾唬。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工万哪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抡秆。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓奕巍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親儒士。 傳聞我的和親對象是個殘疾皇子的止,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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

  • 內(nèi)存管理 簡述OC中內(nèi)存管理機制。與retain配對使用的方法是dealloc還是release着撩,為什么诅福?需要與a...
    丶逐漸閱讀 1,948評論 1 16
  • 29.理解引用計數(shù) Objective-C語言使用引用計數(shù)來管理內(nèi)存,也就是說拖叙,每個對象都有個可以遞增或遞減的計數(shù)...
    Code_Ninja閱讀 1,470評論 1 3
  • 內(nèi)存管理 ARC處理原理 ARC是Objective-C編譯器的特性氓润,而不是運行時特性或者垃圾回收機制,ARC所做...
    b485c88ab697閱讀 11,180評論 3 47
  • iOS內(nèi)存管理 概述 什么是內(nèi)存管理 應用程序內(nèi)存管理是在程序運行時分配內(nèi)存(比如創(chuàng)建一個對象,會增加內(nèi)存占用)與...
    蚊香醬閱讀 5,694評論 8 119
  • 內(nèi)存管理是程序在運行時分配內(nèi)存憋沿、使用內(nèi)存,并在程序完成時釋放內(nèi)存的過程沪猴。在Objective-C中辐啄,也被看作是在眾...
    蹲瓜閱讀 3,001評論 1 8