亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

iOS/OSX內存管理(1):基本概念與原理

2019-11-14 17:54:36
字體:
來源:轉載
供稿:網友

在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對象的動作
開燈創建一個對象并獲取它的所有權(ownership)
使用燈獲取對象的所有權
不使用燈放棄對象的所有權
關燈釋放對象

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


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

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

Objective-C對象的動作Objective-C對象的方法
1. 創建一個對象并獲取它的所有權alloc/new/copy/mutableCopy (RC = 1)
2. 獲取對象的所有權retain (RC + 1)
3. 放棄對象的所有權release (RC – 1)
4. 釋放對象dealloc (RC = 0 ,此時會調用該方法)

當你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的寫法)實例代碼如下:

 
 
1
2
3
4
5
6
7
8
9
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
// put object into pool
id obj = [[NSObject alloc] init];
[obj autorelease];
 
[pool drain];
 
/* 超過autorelease pool作用域范圍時,obj會自動調用release方法 */

由于放在autorelease pool的對象并不會馬上釋放,如果有大量圖片數據放在這里的話,將會導致內存不足。

 
 
1
for (int i = 0; i

 

ARC管理方法

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

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

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

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

__strong ownership qualifier

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

 
 
1
2
3
4
5
{
    NSString *text = @"Hello, world";    //@"Hello, world"對象的RC=1
    NSLog(@"%@", text);
    [text release];                      //@"Hello, world"對象的RC=0
}

而如果是使用ARC方式的話,就text對象無需調用release方法,而是當text變量超過作用域時,編譯器來自動加入[text release]方法來釋放內存

 
 
1
2
3
4
5
6
7
{
    NSString *text = @"Hello, world";    //@"Hello, world"對象的RC=1
    NSLog(@"%@", text);
}
/*
*  當text超過作用域時,@"Hello, world"對象會自動釋放,RC=0
*/

而當你將text賦值給其他變量anotherText時,MRC需要retain一下來持有所有權,當textanotherText使用完之后,各個調用release方法來釋放。

 
 
1
2
3
4
5
6
7
8
9
10
11
{
    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的話,并不需要調用retainrelease方法來持有跟釋放對象。

 
 
1
2
3
4
5
6
7
8
9
10
{
    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語句來釋放變量指向之前的對象。例如:

 
 
1
2
3
4
5
6
7
8
9
{
    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的所有權

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

Objective-C對象的動作Objective-C對象的方法
1. 創建一個對象并獲取它的所有權alloc/new/copy/mutableCopy (RC = 1)
2. 獲取對象的所有權retain (RC + 1)
3. 放棄對象的所有權release (RC – 1)
4. 釋放對象dealloc (RC = 0 ,此時會調用該方法)

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

  • 對于規則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:

 
 
1
2
3
4
5
@interface Test : NSObject
 
@property (strong, nonatomic) id objc;
 
@end

 

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
    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。例如:

 
 
1
2
__weak NSString *text = @"Sam Lau";
NSLog(@"%@", text);

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

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

 
 
1
2
3
4
5
@interface Test : NSObject
 
@property (weak, nonatomic) id objc;
 
@end

 

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
{
    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)。例子如下:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__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的話,兩個打印結果將不同

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
__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之前的寫法如下:

 
 
1
2
3
4
5
6
7
8
9
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 
// put object into pool
id obj = [[NSObject alloc] init];
[obj autorelease];
 
[pool drain];
 
/* 超過autorelease pool作用域范圍時,obj會自動調用release方法 */

引入ARC之后,寫法比之前更加簡潔:

 
 
1
2
3
@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使用了它:

 
 
1
2
3
4
5
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIapplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

 

Property(屬性)

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

 
 
1
@property (strong, nonatomic) NSString *text;

下面有張表來展示property modifier與ownership qualifier的對應關系

Property modifierOwnership qualifier
strong__strong
retain__strong
copy__strong
weak__weak
assign__unsafe_unretained
unsafe_unretained__unsafe_unretained

總結

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

 

全能程序員交流QQ群290551701,聚集很多互聯網精英,技術總監,架構師,項目經理!開源技術研究,歡迎業內人士,大牛及新手有志于從事IT行業人員進入!


上一篇:SDWebImage的簡單使用

下一篇:內存模型

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩欧美中文字幕在线播放| 亚洲国产女人aaa毛片在线| 日韩高清av一区二区三区| 亚洲国产成人爱av在线播放| 成人性生交大片免费看视频直播| 日本一欧美一欧美一亚洲视频| 青青草原一区二区| 欧美精品少妇videofree| 欧美日韩一区二区三区在线免费观看| 欧美一区亚洲一区| 久久精品中文字幕电影| 亚洲国产女人aaa毛片在线| 国产精品久久久久999| 超在线视频97| 美女福利精品视频| 中文字幕精品www乱入免费视频| 国产一区二区黄| 久久久久女教师免费一区| 久久精品视频亚洲| 欧美精品福利在线| 国产精品女人久久久久久| 久久人91精品久久久久久不卡| 国产精品色视频| 亚洲午夜女主播在线直播| 性金发美女69hd大尺寸| 亚洲精品成人久久电影| 日韩精品中文字幕视频在线| 超碰日本道色综合久久综合| 91在线国产电影| 欧美成人免费视频| 日韩中文字幕不卡视频| 777国产偷窥盗摄精品视频| 高跟丝袜欧美一区| 51午夜精品视频| 国产69精品久久久久9| 精品久久久久久久中文字幕| 亚洲va男人天堂| 欧洲精品毛片网站| 久久青草福利网站| 欧美亚洲国产日本| 国产高清视频一区三区| 91精品国产高清久久久久久久久| 欧美高清在线视频观看不卡| 在线看国产精品| 青青草99啪国产免费| 日韩av在线免费观看| 夜夜嗨av一区二区三区四区| 欧美国产视频日韩| 丁香五六月婷婷久久激情| 欧美激情网站在线观看| 欧美日韩中文字幕| 日韩在线观看av| 91久久精品国产91久久| 亚洲精品福利在线| 国产精品入口免费视| 欧美理论电影在线播放| 亚洲福利视频免费观看| 亚洲日本欧美中文幕| 亚洲人av在线影院| 国产精品久久久91| 国产精品扒开腿做| 2018日韩中文字幕| 亚洲国产精品久久久久秋霞不卡| 亚洲精品电影网在线观看| 欧美精品18videosex性欧美| 欧美日韩精品在线播放| 欧美在线不卡区| 在线观看91久久久久久| 亚洲精品国产精品自产a区红杏吧| 亚洲精品视频免费在线观看| 国产精品一二区| 91精品国产一区| 亚洲深夜福利视频| 日本一区二区三区四区视频| 成人国产精品一区| 欧美区在线播放| 国产欧美va欧美va香蕉在线| 亚洲黄色av女优在线观看| 国产999精品| 国产一区红桃视频| 亚洲欧洲第一视频| 欧美性极品xxxx娇小| 18性欧美xxxⅹ性满足| 主播福利视频一区| 中文字幕国产精品| 久久精品国产一区二区三区| 亚洲成人在线网| 亚洲毛茸茸少妇高潮呻吟| 亚洲视频专区在线| 日韩美女在线看| 亚洲欧美国产精品va在线观看| 欧美乱大交xxxxx另类电影| 日韩av不卡电影| 欧美插天视频在线播放| 这里只有精品在线播放| 成人欧美在线视频| 国产精品一香蕉国产线看观看| 国内揄拍国内精品少妇国语| 成人欧美一区二区三区在线湿哒哒| 久久频这里精品99香蕉| 国产精品欧美一区二区| 国外成人在线视频| 中文字幕日韩av综合精品| 色哟哟亚洲精品一区二区| 日韩免费观看网站| 亚洲www在线| 日韩国产在线看| 成人激情黄色网| 欧美一区二区大胆人体摄影专业网站| 国产精品69av| 国产精品久久久久av免费| 日韩成人在线网站| 伊人久久大香线蕉av一区二区| 91tv亚洲精品香蕉国产一区7ujn| 欧美在线播放视频| 国产精品久久一区主播| 日韩在线视频观看正片免费网站| 成人免费网站在线看| 国产精品户外野外| 久久免费视频观看| 91国内产香蕉| 亚洲欧美日本精品| 色av中文字幕一区| 国产精品视频色| 欧美另类69精品久久久久9999| 欧美精品手机在线| 日韩a**中文字幕| 久久91亚洲精品中文字幕| 91夜夜未满十八勿入爽爽影院| 欧美色欧美亚洲高清在线视频| 亚洲free嫩bbb| 亚洲精品电影网在线观看| 久久亚洲综合国产精品99麻豆精品福利| 亚洲天堂影视av| 另类专区欧美制服同性| 亚洲天堂成人在线视频| 久久久国产精品x99av| 亚洲欧美综合区自拍另类| 欧美激情性做爰免费视频| 自拍亚洲一区欧美另类| 91高潮精品免费porn| 久久精品91久久香蕉加勒比| 亚洲人午夜精品免费| 久久久久久av| 久久99精品视频一区97| 在线日韩中文字幕| 国产精品日日做人人爱| 黑人精品xxx一区一二区| 国产精品高潮呻吟久久av野狼| 国产精品扒开腿做爽爽爽男男| 久久精品国产96久久久香蕉| 欧美精品精品精品精品免费| 亚洲精品在线视频| 成人a级免费视频| 成人看片人aa| 精品福利在线观看| 国产精品一二三视频| 欧美激情一区二区久久久| 久久婷婷国产麻豆91天堂| 欧美成人精品一区二区| 91久久夜色精品国产网站| 在线视频免费一区二区| 欧美大片第1页| 国产精品男人的天堂|