日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS/OS X内存管理(一):基本概念与原理

發布時間:2025/7/25 编程问答 15 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS/OS X内存管理(一):基本概念与原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

iOS/OS X內存管理(一):基本概念與原理

發表于21小時前|?1585次閱讀| 來源CSDN|?8?條評論| 作者劉耀柱

移動開發iOSObjective-C內存管理內存泄露局部變量開發經驗 allowtransparency="true" frameborder="0" scrolling="no" src="http://hits.sinajs.cn/A1/weiboshare.html?url=http%3A%2F%2Fwww.csdn.net%2Farticle%2F2015-11-12%2F2826198%3Fref%3Dmyread&type=3&count=&appkey=&title=%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E6%98%AF%E5%9C%A8%E7%A8%8B%E5%BA%8F%E9%9C%80%E8%A6%81%E6%97%B6%E7%A8%8B%E5%BA%8F%E5%91%98%E5%88%86%E9%85%8D%E4%B8%80%E6%AE%B5%E5%86%85%E5%AD%98%E7%A9%BA%E9%97%B4%EF%BC%8C%E8%80%8C%E5%BD%93%E4%BD%BF%E7%94%A8%E5%AE%8C%E4%B9%8B%E5%90%8E%E5%B0%86%E5%AE%83%E9%87%8A%E6%94%BE%E3%80%82%E5%A6%82%E6%9E%9C%E7%A8%8B%E5%BA%8F%E5%91%98%E5%AF%B9%E5%86%85%E5%AD%98%E8%B5%84%E6%BA%90%E4%BD%BF%E7%94%A8%E4%B8%8D%E5%BD%93%EF%BC%8C%E6%9C%89%E6%97%B6%E4%B8%8D%E4%BB%85%E4%BC%9A%E9%80%A0%E6%88%90%E5%86%85%E5%AD%98%E8%B5%84%E6%BA%90%E6%B5%AA%E8%B4%B9%EF%BC%8C%E7%94%9A%E8%87%B3%E4%BC%9A%E5%AF%BC%E8%87%B4%E7%A8%8B%E5%BA%8Fcrach%E3%80%82%E6%9C%AC%E6%96%87%E4%BD%9C%E8%80%85%E4%BB%8E%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%BC%80%E5%A7%8B%EF%BC%8C%E5%89%96%E6%9E%90%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E7%9A%84%E6%96%B9%E6%B3%95%E4%B8%8E%E9%97%AE%E9%A2%98%E3%80%82&pic=&ralateUid=&language=zh_cn&rnd=1447398728228" width="22" height="16">摘要:內存管理是在程序需要時程序員分配一段內存空間,而當使用完之后將它釋放。如果程序員對內存資源使用不當,有時不僅會造成內存資源浪費,甚至會導致程序crach。本文作者從基本概念開始,剖析內存管理的方法與問題。

CSDN移動將持續為您優選移動開發的精華內容,共同探討移動開發的技術熱點話題,涵蓋移動應用、開發工具、移動游戲及引擎、智能硬件、物聯網等方方面面。如果您想投稿、尋求《近匠》報道,或給文章挑錯,歡迎發送郵件至tangxy#csdn.net(請把#改成@)。?


在Objective-C的內存管理中,其實就是引用計數(reference count)的管理。內存管理就是在程序需要時程序員分配一段內存空間,而當使用完之后將它釋放。如果程序員對內存資源使用不當,有時不僅會造成內存資源浪費,甚至會導致程序crach。我們將會從引用計數和內存管理規則等基本概念開始,然后講述有哪些內存管理方法,最后注意有哪些常見內存問題。?

?

memory management from apple document?

基本概念

引用計數(Reference Count)

為了解釋引用計數,我們做一個類比:員工在辦公室使用燈的情景。

?

引用Pro Multithreading and Memory Management for iOS and OS X的圖

  • 當第一個人進入辦公室時,他需要使用燈,于是開燈,引用計數為1;
  • 當另一個人進入辦公室時,他也需要燈,引用計數為2;每當多一個人進入辦公室時,引用計數加1;
  • 當有一個人離開辦公室時,引用計數減1,當引用計數為0時,也就是最后一個人離開辦公室時,他不再需要使用燈,關燈離開辦公室。

內存管理規則

從上面員工在辦公室使用燈的例子,我們對比一下燈的動作與Objective-C對象的動作有什么相似之處:

?

因為我們是通過引用計數來管理燈,那么我們也可以通過引用計數來管理使用Objective-C對象。

?

引用Pro Multithreading and Memory Management for iOS and OS X的圖

而Objective-C對象的動作對應有哪些方法以及這些方法對引用計數有什么影響?

?

當你alloc一個對象objc,此時RC=1;在某個地方你又retain這個對象objc,此時RC加1,也就是RC=2;由于調用alloc/retain一次,對應需要調用release一次來釋放對象objc,所以你需要release對象objc兩次,此時RC=0;而當RC=0時,系統會自動調用dealloc方法釋放對象。

Autorelease Pool

在開發中,我們常常都會使用到局部變量,局部變量一個特點就是當它超過作用域時,就會自動釋放。而autorelease pool跟局部變量類似,當執行代碼超過autorelease pool塊時,所有放在autorelease pool的對象都會自動調用release。它的工作原理如下:

  • 創建一個NSAutoreleasePool對象;
  • 在autorelease pool塊的對象調用autorelease方法;
  • 釋放NSAutoreleasePool對象。

?

引用Pro Multithreading and Memory Management for iOS and OS X的圖

iOS 5/OS X Lion前的(等下會介紹引入ARC的寫法)實例代碼如下:

[cpp]?view plaincopy
  • NSAutoreleasePool?*pool?=?[[NSAutoreleasePool?alloc]?init];??
  • ??
  • //?put?object?into?pool??
  • id?obj?=?[[NSObject?alloc]?init];??
  • [obj?autorelease];??
  • ??
  • [pool?drain];??
  • ??
  • /*?超過autorelease?pool作用域范圍時,obj會自動調用release方法?*/??
  • 由于放在autorelease pool的對象并不會馬上釋放,如果有大量圖片數據放在這里的話,將會導致內存不足。?

    [cpp]?view plaincopy
  • for?(int?i?=?0;?i?<?numberOfImages;?i++)??
  • {??
  • ??????/*???處理圖片,例如加載?
  • ???????*???太多autoreleased?objects存在?
  • ???????*???由于NSAutoreleasePool對象沒有被釋放?
  • ???????*???在某個時刻,會導致內存不足??
  • ???????*/??
  • }??
  • ARC管理方法

    iOS/OS X內存管理方法有兩種:手動引用計數(Manual Reference Counting)和自動引用計數(Automatic Reference Counting)。從OS X Lion和iOS 5開始,不再需要程序員手動調用retain和release方法來管理Objective-C對象的內存,而是引入一種新的內存管理機制Automatic Reference Counting(ARC),簡單來說,它讓編譯器來代替程序員來自動加入retain和release方法來持有和放棄對象的所有權。

    在ARC內存管理機制中,id和其他對象類型變量必須是以下四個ownership qualifiers其中一個來修飾:

    • __strong(默認,如果不指定其他,編譯器就默認加入)
    • __weak
    • __unsafe_unretained
    • __autoreleasing

    所以在管理Objective-C對象內存的時候,你必須選擇其中一個,下面會用一些列子來逐個解釋它們的含義以及如何選擇它們。

    __strong ownership qualifier

    如果我想創建一個字符串,使用完之后將它釋放調用,使用MRC管理內存的寫法應該是這樣:

    [cpp]?view plaincopy
  • {??
  • ????NSString?*text?=?@"Hello,?world";????//@"Hello,?world"對象的RC=1??
  • ????NSLog(@"%@",?text);??
  • ????[text?release];??????????????????????//@"Hello,?world"對象的RC=0??
  • }??
  • 而如果是使用ARC方式的話,就text對象無需調用release方法,而是當text變量超過作用域時,編譯器來自動加入[text release]方法來釋放內存。?

    [cpp]?view plaincopy
  • {??
  • ????NSString?*text?=?@"Hello,?world";????//@"Hello,?world"對象的RC=1??
  • ????NSLog(@"%@",?text);??
  • }??
  • /*?
  • ?*??當text超過作用域時,@"Hello,?world"對象會自動釋放,RC=0?
  • ?*/??
  • 而當你將text賦值給其他變量anotherText時,MRC需要retain一下來持有所有權,當text和anotherText使用完之后,各個調用release方法來釋放。?

    [cpp]?view plaincopy
  • {??
  • ????NSString?*text?=?@"Hello,?world";????//@"Hello,?world"對象的RC=1??
  • ????NSLog(@"%@",?text);??
  • ??
  • ????NSString?*anotherText?=?text;????????//@"Hello,?world"對象的RC=1??
  • ????[anotherText?retain];????????????????//@"Hello,?world"對象的RC=2??
  • ????NSLog(@"%@",?anotherText);??
  • ??
  • ????[text?release];??????????????????????//@"Hello,?world"對象的RC=1??
  • ????[anotherText?release];???????????????//@"Hello,?world"對象的RC=0??
  • }??
  • 而使用ARC的話,并不需要調用retain和release方法來持有跟釋放對象。?

    [cpp]?view plaincopy
  • {??
  • ????NSString?*text?=?@"Hello,?world";????//@"Hello,?world"對象的RC=1??
  • ????NSLog(@"%@",?text);??
  • ??
  • ????NSString?*anotherText?=?text;????????//@"Hello,?world"對象的RC=2??
  • ????NSLog(@"%@",?anotherText);??
  • }??
  • /*?
  • ?*??當text和anotherText超過作用域時,會自動調用[text?release]和[anotherText?release]方法,?@"Hello,?world"對象的RC=0?
  • ?*/??
  • 除了當__strong變量超過作用域時,編譯器會自動加入release語句來釋放內存,如果你將__strong變量重新賦給它其他值,那么編譯器也會自動加入release語句來釋放變量指向之前的對象。例如:?

    [cpp]?view plaincopy
  • {??
  • ????NSString?*text?=?@"Hello,?world";????//@"Hello,?world"對象的RC=1??
  • ????NSString?*anotherText?=?text;????????//@"Hello,?world"對象的RC=2??
  • ????NSString?*anotherText?=?@"Sam?Lau";??//?由于anotherText對象引用另一個對象@"Sam?Lau",那么就會自動調用[anotherText?release]方法,使得@"Hello,?world"對象的RC=1,?@"Sam?Lau"對象的RC=1??
  • }??
  • /*?
  • ?*??當text和anotherText超過作用域時,會自動調用[text?release]和[anotherText?release]方法,?
  • ?*??@"Hello,?world"對象的RC=0和@"Sam?Lau"對象的RC=0?
  • ?*/??
  • 如果變量var被__strong修飾,當變量var指向某個對象objc,那么變量var持有某個對象objc的所有權。?

    前面已經提過內存管理的四條規則:?

    ?

    我們總結一下編譯器是按以下方法來實現的:

    • 對于規則1和規則2,是通過__strong變量來實現;
    • 對于規則3來說,當變量超過它的作用域或被賦值或成員變量被丟棄時就能實現;
    • 對于規則4,當RC=0時,系統就會自動調用。

    __weak ownership qualifier

    其實編譯器根據__strong修飾符來管理對象內存。但是__strong并不能解決引用循環(Reference Cycle)問題:對象A持有對象B,反過來,對象B持有對象A;這樣會導致不能釋放內存造成內存泄露問題。

    引用Pro Multithreading and Memory Management for iOS and OS X的圖

    舉一個簡單的例子,有一個類Test有個屬性objc,有兩個對象test1和test2的屬性objc互相引用test1和test2:

    [cpp]?view plaincopy
  • @interface?Test?:?NSObject??
  • @property?(strong,?nonatomic)?id?objc;??
  • @end??
  • [cpp]?view plaincopy
  • {??
  • ????Test?*test1?=?[Test?new];????????/*?對象a?*/??
  • ????/*?test1有一個強引用到對象a?*/??
  • ??
  • ????Test?*test2?=?[Test?new];????????/*?對象b?*/??
  • ????/*?test2有一個強引用到對象b?*/??
  • ??
  • ????test1.objc?=?test2;??????????????/*?對象a的成員變量objc有一個強引用到對象b?*/??
  • ????test2.objc?=?test1;??????????????/*?對象b的成員變量objc有一個強引用到對象a?*/??
  • }??
  • /*???當變量test1超過它作用域時,它指向a對象會自動release?
  • ?*???當變量test2超過它作用域時,它指向b對象會自動release?
  • ?*????
  • ?*???此時,b對象的objc成員變量仍持有一個強引用到對象a?
  • ?*???此時,a對象的objc成員變量仍持有一個強引用到對象b?
  • ?*???于是發生內存泄露?
  • ?*/??
  • 如何解決?于是我們引用一個__weakownership qualifier,被它修飾的變量都不持有對象的所有權,而且當變量指向的對象的RC為0時,變量設置為nil。例如:?

    [cpp]?view plaincopy
  • __weak?NSString?*text?=?@"Sam?Lau";??
  • NSLog(@"%@",?text);??
  • 由于text變量被__weak修飾,text并不持有@"Sam Lau"對象的所有權,@"Sam Lau"對象一創建就馬上被釋放,并且編譯器給出警告??,所以打印結果為(null)。

    所以,針對剛才的引用循環問題,只需要將Test類的屬性objc設置weak修飾符,那么就能解決。

    [cpp]?view plaincopy
  • @interface?Test?:?NSObject??
  • @property?(weak,?nonatomic)?id?objc;??
  • @end??
  • [cpp]?view plaincopy
  • {??
  • ????Test?*test1?=?[Test?new];????????/*?對象a?*/??
  • ????/*?test1有一個強引用到對象a?*/??
  • ??
  • ????Test?*test2?=?[Test?new];????????/*?對象b?*/??
  • ????/*?test2有一個強引用到對象b?*/??
  • ??
  • ????test1.objc?=?test2;??????????????/*?對象a的成員變量objc不持有對象b?*/??
  • ????test2.objc?=?test1;??????????????/*?對象b的成員變量objc不持有對象a?*/??
  • }??
  • /*???當變量test1超過它作用域時,它指向a對象會自動release?
  • ?*???當變量test2超過它作用域時,它指向b對象會自動release?
  • ?*/??
  • __unsafe_unretained ownership qualifier

    __unsafe_unretained ownership qualifier,正如名字所示,它是不安全的。它跟__weak相似,被它修飾的變量都不持有對象的所有權,但當變量指向的對象的RC為0時,變量并不設置為nil,而是繼續保存對象的地址;這樣的話,對象有可能已經釋放,但繼續訪問,就會造成非法訪問(Invalid Access)。例子如下:

    [cpp]?view plaincopy
  • __unsafe_unretained?id?obj0?=?nil;??
  • {??
  • ????id?obj1?=?[[NSObject?alloc]?init];?????//?對象A??
  • ????/*?由于obj1是強引用,所以obj1持有對象A的所有權,對象A的RC=1?*/??
  • ????obj0?=?obj1;??
  • ????/*?由于obj0是__unsafe_unretained,它不持有對象A的所有權,但能夠引用它,對象A的RC=1?*/??
  • ????NSLog(@"A:?%@",?obj0);??
  • }??
  • /*?當obj1超過它的作用域時,它指向的對象A將會自動釋放?*/??
  • NSLog(@"B:?%@",?obj0);??
  • /*?由于obj0是__unsafe_unretained,當它指向的對象RC=0時,它會繼續保存對象的地址,所以兩個地址相同?*/??
  • 打印結果是內存地址相同:?

    ?

    如果將__unsafe_unretained改為weak的話,兩個打印結果將不同。?

    [cpp]?view plaincopy
  • __weak?id?obj0?=?nil;??
  • {??
  • ????id?obj1?=?[[NSObject?alloc]?init];?????//?對象A??
  • ????/*?由于obj1是強引用,所以obj1持有對象A的所有權,對象A的RC=1?*/??
  • ????obj0?=?obj1;??
  • ????/*?由于obj0是__unsafe_unretained,它不持有對象A的所有權,但能夠引用它,對象A的RC=1?*/??
  • ????NSLog(@"A:?%@",?obj0);??
  • }??
  • /*?當obj1超過它的作用域時,它指向的對象A將會自動釋放?*/??
  • NSLog(@"B:?%@",?obj0);??
  • /*?由于obj0是__weak,?當它指向的對象RC=0時,它會自動設置為nil,所以兩個打印結果將不同*/??
  • ?

    __autoreleasing ownership qualifier

    引入ARC之后,讓我們看看autorelease pool有哪些變化。沒有ARC之前的寫法如下:

    [cpp]?view plaincopy
  • NSAutoreleasePool?*pool?=?[[NSAutoreleasePool?alloc]?init];??
  • //?put?object?into?pool??
  • id?obj?=?[[NSObject?alloc]?init];??
  • [obj?autorelease];??
  • [pool?drain];??
  • /*?超過autorelease?pool作用域范圍時,obj會自動調用release方法?*/??
  • 引入ARC之后,寫法比之前更加簡潔:?

    [cpp]?view plaincopy
  • @autoreleasepool?{??
  • ????id?__autoreleasing?obj?=?[[NSObject?alloc]?init];??
  • }??
  • 相比之前的創建、使用和釋放NSAutoreleasePool對象,現在你只需要將代碼放在@autoreleasepool塊即可。你也不需要調用autorelease方法了,只需要用__autoreleasing修飾變量即可。?

    ?

    引用Pro Multithreading and Memory Management for iOS and OS X的圖?

    但是我們很少或基本上不使用autorelease pool。當我們使用XCode創建工程后,有一個app的入口文件main.m使用了它:?

    [cpp]?view plaincopy
  • int?main(int?argc,?char?*?argv[])?{??
  • ????@autoreleasepool?{??
  • ????????return?UIApplicationMain(argc,?argv,?nil,?NSStringFromClass([AppDelegate?class]));??
  • ????}??
  • }??
  • Property(屬性)

    有了ARC之后,新的property modifier也被引入到Objective-C類的property,例如:

    [cpp]?view plaincopy
  • @property?(strong,?nonatomic)?NSString?*text;??
  • 下面有張表來展示property modifier與ownership qualifier的對應關系:


    總結

    要想掌握iOS/OS X的內存管理,首先要深入理解引用計數(Reference Count)這個概念以及內存管理的規則;在沒引入ARC之前,我們都是通過retain和release方法來手動管理內存,但引入ARC之后,我們可以借助編譯器來幫忙自動調用retain和release方法來簡化內存管理和減低出錯的可能性。雖然__strong修飾符能夠執行大多數內存管理,但它不能解決引用循環(Reference Cycle)問題,于是又引入另一個修飾符__weak。被__strong修飾的變量都持有對象的所有權,而被__weak修飾的變量并不持有對象所有權。下篇我們介紹使用工具如何解決常見內存問題:野指針和內存泄露。

    參考資料

    • Pro Multithreading and Memory Management for iOS and OS X
    • Advanced Memory Management Programming Guide

    作者簡介:劉耀柱(@Sam_Lau_Dev),iOS Developer兼業余Designer,參與開發技術前線iOS項目翻譯,個人博客:http://www.jianshu.com/users/256fb15baf75/latest_articles,GitHub:https://github.com/samlaudev。?

    第一時間掌握最新移動開發相關信息和技術,請關注mobilehub公眾微信號(ID: mobilehub)。

    總結

    以上是生活随笔為你收集整理的iOS/OS X内存管理(一):基本概念与原理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。