iOS開發-記憶體管理,ios開發記憶體管理

來源:互聯網
上載者:User

iOS開發-記憶體管理,ios開發記憶體管理

記憶體管理

對於這篇呢,其實現在都是ARC模式,正常狀態下基本不用我們去手動釋放記憶體,所以如果不是要面試呀、裝逼或者紮實功底的,就先別看了或者瞭解下即可,因為像面試時,有些面試官想看你的基礎時,就有些人會問,現在工作基本不會用到。

 

學習目標

1. 掌握記憶體管理的原理

2. 掌握手動記憶體管理

 

===============================================

1.需要理解的知識

1.1記憶體管理

1.1.1 C的記憶體管理,以及麻煩之處

char *p = (char *)malloc(100*sizeof (char)); 

這是C的動態記憶體分配,我們手動跟系統申請了100個位元組的記憶體;或者說系統在堆裡開闢了100個位元組的空間,並將這個空間的首地址返回給指標變數p。

strcpy(p,"Hello World!");

將字串拷貝給指標變數p指向的記憶體空間。

puts(p);

將p指標指向的記憶體空間裡的字串列印出來。

free(p);

使用完成後,手動跟系統釋放記憶體空間;或者說系統回收空間。

如上就是C裡簡單的記憶體管理。

C的記憶體管理,我們手動申請,手動釋放。這樣來看,我們只需要注意兩個問題就好了:

1,申請記憶體,使用完成後需要釋放,如果不釋放會造成記憶體泄露。

2,不能多次釋放,如果多次釋放,則會崩潰。

但是,如果項目比較複雜,需要有幾十上百號人一起分工完成,就很容易出現問題。

比方說我們開闢了一塊記憶體空間,裡存放了一塊很有用的資料。但是,這個資料不只有我在這一塊代碼裡用,甚至有多個人,在程式的多個地方使用。這樣造成的結果就是,就算我使用完成這塊記憶體,我也不能去釋放他,因為我不能確定,別人在別的地方是否還需要使用這塊記憶體。記憶體泄露在所難免了。

 

 

 

 

OC的記憶體管理:

1.1.2 引用計數(retainCount)

對於一塊動態申請的記憶體,有一個人(指標)使用,就給這個記憶體的計數器加1,使用完成後,就給這個計數器減1,當這個記憶體的引用計數為0了,我們再釋放他,這樣,上面的問題就解決了。OC,就是使用引用計數這種方式來管理記憶體的。

 

 

 

1.1.3 記憶體管理的黃金法則

對於引用計數來說,有一套記憶體管理的黃金法則:

The basic rule to apply is everything that increases the reference counter with alloc, [mutable]copy[withZone:] or retain is in charge of the corresponding [auto]release.

如果對一個對象使用了alloc、copy、mutablecopy、retain,new,那麼你必須使用

相應的release或者autorelease。

通俗一點的說法就是誰汙染誰治理。

 

 

 

            

1.1.4 objective-C的記憶體管理遵守下面這個簡單的策略:

1.你擁有你建立的對象,也就是說建立的對象(使用alloc,new,copy或者mutalbeCopy等方法)的初始引用計數是1。

2.給對象發送retain訊息後,你擁有了這個對象 ,retainCount+1

3.當你不需要使用該對象時,發送release或者autorelease訊息放棄這個對象

4.不要對你不擁有的對象發送“放棄”的訊息

 

 

 

 

 

1.1.4 MRC和ARC

ARC Automatic Reference Counting,自動引用計數,由xcode,幫我們去管理記憶體。

MRC Manual  Reference Counting,手動引用計數,我們手動管理記憶體。

 

Xcode 5.0  版本以後預設是ARC模式,

 

 

 

 

 

 

 

1.1.5 如何將工程改為MRC

xcode5,工程建立的時候是ARC的,我們如果想要MRC,需要進行如下設定。

選中工程 - target - Bulid Settings -Automatic Reference Counting改為NO。

 

 

 

1.1.6 ARC執行了新的規則

 

● 開發人員不能顯示調用dealloc;不能實現和調用retain、release、retainCount和autorelease。

禁止使用@selector(retain),@selector(release)等等。

開發人員仍可以實現dealloc方法,如果你想管理資源而不是變數。

ARC中自訂的dealloc方法,不需要調用[super dealloc](其實這樣做就會導致編譯錯誤),編譯器會強制自動連結到父類。

開發人員仍可以對Core Foundation-style對象,使用CFRetain,CFRelease和其他相關方法。

 

● 開發人員不能使用NSAutoreleasePool對象。ARC下使用@autoreleasepool,它比NSAtuoreleasePool更有效率。

 

為了配合手動引用計數,ARC的方法命名有限制:

● 訪問器方法不能已new開頭,反過來就是:開發人員不能聲明一個已new開頭的屬性,除非你給你指定一個getter

 

// 不正確 @property NSString *newTitle;       // 正確 @property (getter=theNewTitle) NSString *newTitle;   

 

 

1.1.7.野指標錯誤形式在Xcode中通常表現為:Thread 1EXC_BAD_ACCESS(code=EXC_I386_GPFLT)錯誤。因為你訪問了一塊已經不屬於你的記憶體。

 

2.需要記住的知識

 

2.1 alloc與release

建立一個Dog類

@interface Dog : NSObject  @end  @implementation Dog  - (void)dealloc  {    NSLog(@"dog dealloc");    [super dealloc];  }  @end

 

  delloc裡的解構函式,當對象銷毀的時候,會自動調用這個方法,我們在這裡重寫這個方法。

  在main函數裡,寫入如下代碼:

   

int main(int argc, const char * argv[])  {    @autoreleasepool {        Dog *dog = [[Dog alloc] init];     }    NSLog(@"程式即將退出");    return 0;  }

 

  從終端列印資訊來看,程式即將退出這條列印之前,已經列印dog dealloc,也就是說在程式運行結束前,dog對象已經銷毀了。這個是ARC,由xcode幫我們管理dog對象。

  將ARC改為MRC,再執行程式,dog對象並沒有銷毀,因為我們現在是手動管理了,我們需要遵守記憶體管理的黃金法則,Dog *dog = [[Dog alloc] init]; 我們需要對dog進行release。將main函數代碼改為如下形式:

int main(int argc, const char * argv[]){    @autoreleasepool {        Dog *dog = [[Dog alloc] init];     [dog release];     }    NSLog(@"程式即將退出");    return 0;}

 

再次執行程式,從列印可以看出,dog對象,已經銷毀。這就是黃金法則,我們對dog進行alloc,就要對dog進行release。

注意,release 並不是銷毀對象,讓對象的引用計數減1,當對象的引用計數為0的時候,自動調用dealloc方法,銷毀對象。

 

 

 

2.2 retain與retainCount

retain,將對象進行保留操作,也就是使對象的引用計數加1。

retainCount,列印一個對象的引用計數。

 

 

 

 

 

2.3 類的複合中使用

在上面代碼中,增加Person類

@interface Person : NSObject {  // 一個人,養了一條狗(持有一條狗)    Dog *_dog;  }  - (void)setDog:(Dog *)dog;  - (Dog *)dog;  @end  @implementation Person  /* 版本1 (有問題) 人並沒有真正持有狗,如果在main函數裡[dog release],讓dog的引用計數減1,就變為0,dog就銷毀了。    - (void)setDog:(Dog *)dog  {    _dog = dog;  }    */   /* 版本2 (有問題) 如果人再持有別的狗,就會造成第一條狗得不到釋放,記憶體泄露。  - (void)setDog:(Dog *)dog  {    _dog = [dog retain];  }    */   /* 版本3 (有問題) 如果本來持有一條狗,又重新設定這條狗,先進行release,這個時候,很可能dog就銷毀了,然後,就沒法再次retain了。  - (void)setDog:(Dog *)dog  {    [_dog release];    _dog = [dog retain];  }    */   // 版本4 OK!,標準寫法  - (void)setDog:(Dog *)dog  {    if (_dog != dog) {         [_dog release];          _dog = [dog retain];      }  }   - (Dog *)dog  {    return _dog;  }   - (void)dealloc  {    NSLog(@"person dealloc");  // 人在銷毀的時候,一併將持有的dog對象銷毀    [_dog release];    [super dealloc];  }

 

 

 

 

//MRC:

黃金法則:

只要使用了alloc/retain/copy/mutableCopy,new, 建立了對象

那麼就必須使用release進行釋放,

———總結一句話就是:誰建立,誰負責釋放

retain   —  使對象的引用計數+1, 如果指標需要去持有這個對象

需要使用retain

retainCount:  返回對象的引用計數值 

release :  — 使對象的引用計數 -1, 而不是釋放對象

dealloc:對象銷毀的時候(也就是retainCount為0的時候)自動調用這個方法

 

 

 

 

 

MRC:

2.4 @property retain,assign,copy展開

2.4.1 retain展開

如上代碼裡,Person的setter和getter方法,也可以用property,寫成如下形式

@property (nonatomic, retain) Dog *dog;

             則會展開如下:

- (void)setDog:(Dog *)dog{  if (_dog != dog)  {    [_dog release];    _dog = [dog retain];  }}

 

 

 

2.4.2 assign展開

  //單一資料型別 ,OC的記憶體管理對於簡單的資料類型 int\float…, 

   

@property (nonatomic, assign) Dog *dog;,assign是直接賦值,則會展開如下:- (void)setDog:(QFDog *)dog{  _dog = dog;}

 

 

 

 

 

  2.4.3 copy展開  , 複製一份原來的對象

 

//copy 多用於字串

@property (nonatomic, copy)NSString *name;           展開如下:- (void)setName:(NSString *)name{  if (_name != name)  {    [_name release];    _name = [name copy];  }}

 

 

 

 

 

 

 

 

2.4 字串記憶體管理

  2.4.1 字串的記憶體管理

  // 對於字串而言,非常不遵守黃金法則! (如果從字串的引用計數來看,亂七八糟!) 這隻是一個表象! 其實內部還是遵循的!!

        // 我們要做的是,我們依舊遵守我們的黃金法則!

       

因此,如果是NSString,我們的property格式寫成如下: @property (nonatomic, copy) NSString *name;

 

2.4.2 copy和mutableCopy

       

 

 

2.5 數組的記憶體管理

 

 

 

 

結論

  1)當我們建立數組的時候,數組會對每個對象進行引用計數加1

  2)當數組銷毀的時候,數組會對每個對象進行引用計數減1

  3)當我們給數組添加對象的時候,會對對象進行引用計數加1

  4)當我們給數組刪除對象的時候,會對對象進行引用計數減1

  總之,誰汙染誰治理,管好自己就可以了(數組內部也遵守記憶體管理)。

 

 

 

 

2.6 autorelease與autoreleasepool

在main函數裡寫如下代碼:

int main(int argc, const char * argv[])  {    @autoreleasepool {           Dog *dog = [[Dog alloc] init];      //dog並沒有馬上銷毀,而是延遲銷毀,將dog對象的擁有權交給了autoreleasepool      [dog autorelease];      //這個是可以列印的,因為列印完dog的引用計數後,dog對象才銷毀      NSLog(@"retainCount = %lu",dog.retainCount);     }    NSLog(@"程式即將退出");    return 0;  }

 

  autoreleasepool相當於一個數組,如果哪個對象發送autorelease訊息,實際將對象的擁有權交給了autoreleasepool;當autoreleasepool銷毀的時候,autoreleasepool裡持有的對象都發送一個release訊息。

 

2.7 加方法的記憶體管理 

  我們用加方法建立的對象,不用我們release,是因為類內部的實現使用了autorelease,延遲釋放

  在Dog類的聲明裡增加一個加方法

  + (id)dog;

  在Dog類的實現裡進行實現

  + (id)dog

  {

注意,這裡不要寫成release,如果是release,那麼剛建立就銷毀了,使用autorelease,使得將對象的擁有權交給了自動釋放池,只要自動釋放池沒有銷毀,dog對象也就不會銷毀。

return [[[Dog alloc] init] autorelease];

  }

 

 

2.8  對於自動記憶體釋放簡單總結一下:

 

 

=====================================

 

ARC模式下的關鍵字:

__strong/__weak/__unsafe_unretain

 

開發人員需要正確修飾變數。使用下面的格式來修飾變數聲明。

 

             類名*  修飾  變數名

             例如:

 

       MyClass * __weak myWeakReference;          MyClass * __unsafe_unretained myUnsafeReference; 

 

 

對應的@property 參數分別為

strong/weak/unsafe_unretain

 

__strong : 強引用,相當於MRC下的retain,指標對對象具有決定的占

                            有,預設情況。

__weak :     弱引用,指標對對象不具有決定的佔有,相當於MRC下的

      assign,對象釋放後,指標賦值為nil。

__unsafe_unretain:弱引用,指標對對象不具有決定的佔有,相當於MRC下的assign,對象釋放後,指標為懸垂指標(不會賦值為nil),可以會出現野指標,不建議使用。

 

@property(nonatomic, strong) xxx

//set 類似於 retain 展開  [name retain]

@property(nonatomic, weak)  xxx

//類似於 assign

@property(nonatomic, unsafe_unretain) xxx

//類似於 assign

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.