你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS開發-內存管理

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 1:EXC_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  關於自動內存釋放復雜總結一下:

  1.                1 autorelease辦法不會改動對象的援用計數器,只是將這個對象放到自動釋放池中;
  2. 自動釋放池本質是當自動釋放池銷毀後調用對象的release辦法,不一定就能銷毀對象(例如假如一個             對象的援用計數器>1則此時就無法銷毀);
  3. 由於自動釋放池最後一致銷毀對象,因而假如一個操作比擬占用內存(對象比擬多或許對象占用資源比擬多),最好不要放到自動釋放池或許思索放到多個自動釋放池;
  4. ObjC中類庫中的靜態辦法普通都不需求手動釋放,外部曾經調用了autorelease辦法;

 

 

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

 

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

 

【iOS開發-內存管理】的相關資料介紹到這裡,希望對您有所幫助! 提示:不會對讀者因本文所帶來的任何損失負責。如果您支持就請把本站添加至收藏夾哦!

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved