你好,歡迎來到IOS教程網

 Ios教程網 >> 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 autorelease方法不會改變對象的引用計數器,只是將這個對象放到自動釋放池中;
自動釋放池實質是當自動釋放池銷毀後調用對象的release方法,不一定就能銷毀對象(例如如果一個       對象的引用計數器>1則此時就無法銷毀);
由於自動釋放池最後統一銷毀對象,因此如果一個操作比較占用內存(對象比較多或者對象占用資源比較多),最好不要放到自動釋放池或者考慮放到多個自動釋放池;
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

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