1. 程式人生 > >OC語言--內存管理

OC語言--內存管理

int 必須 pop mil -c -h imp settings 寫法

1.內存管理原理的介紹

1.1C的內存管理

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。不能多次釋放。假設多次釋放,則會崩潰。

可是,假設項目比較復雜,須要有幾十上百號人一起分工完畢,就非常easy出現故障。

例如說我們開辟了一塊內存空間。裏面存放了一塊非常實用的數據。可是,這個數據不僅僅有我在這一塊代碼裏用,甚至有多個人,在程序的多個地方使用。這樣造成的結果就是,就算我使用完畢這塊內存,我也不能去釋放他,由於我不能確定,別人在別的地方是否還須要使用這塊內存。內存泄露在所難免了。

2.OC的內存管理方式:引用計數

2.1引用計數

對於一塊動態申請的內存,有一個人(指針)使用。

就給這個內存的計數器(該計數器在該對象中)加1,使用完畢後。就給這個計數器減1,當這個內存的引用計數為0了,我們則釋放他,這樣,上面的問題就攻克了。

OC。就是使用引用計數這樣的方式來管理內存的。

2.2內存管理的黃金法則

對於引用計數來說。有一套內存管理的黃金法則:

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

假設對一個對象使用了alloc、[mutable]copy、retain,那麽你必須使用對應的release或者autorelease。

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

2.3MRC和ARC

ARC Automatic Reference Counting,自己主動引用計數,由xcode。幫我們去管理內存。

MRC Manual Reference Counting,手動引用計數,由我們手動管理內存。

但就眼下來看,非常多公司還是使用MRC.

2.4 怎樣將project改為MRC

xcode5。project創建的時候是ARC的。我們假設想要MRC,須要進行例如以下設置。

選中project - >target - >Bulid Settings - >搜索:automatic reference counting或auto。將Objective-C Automatic Reference Counting改為NO。

3.手動內存管理的操作(MRC)

3.1alloc與release

創建一個新的project,先別將內存管理改為手動

創建一個Dog類

@interface Dog : NSObject

  @end

 

  @implementation Dog

  - (void)dealloc

  {

    NSLog(@"dog dealloc");

    [super dealloc];

  }

  @end

dealloc裏的析構函數,當對象銷毀的時候。會自己主動調用這種方法。我們在這裏重寫這種方法。

在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方法並銷毀對象。

3.2 retain與retainCount

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

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

將main函數代碼改為例如以下形式:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

//此時打印的結果,retainCount值為1,

//也就是我們alloc。創建dog對象時,對象的引用計數為1

NSLog(@"dog retainCount = %lu",[dog retainCount]);

 

//dog1指針要使用(引用)dog對象,

//此時,為避免dog對象進行release。

//使得引用計數減1變為0,銷毀對象。

//我們進行了retain操作。

Dog *dog1 = [dog retain];

//此時打印的結果,retainCount值為2

NSLog(@"dog retainCount = %lu",[dog retainCount]);

Dog *dog2 = [dog retain];

//此時打印的結果,dog,dog1,dog2,retainCount值都為3,

//由於這三個指針指向同一個對象。

NSLog(@"dog retainCount = %lu",[dog retainCount]);

NSLog(@"dog1 retainCount = %lu",[dog1 retainCount]);

NSLog(@"dog2 retainCount = %lu",[dog2 retainCount]);

 

 //release 並非銷毀對象,讓對象的引用計數減1

 [dog release];

//此時打印的結果,dog,dog1,dog2,retainCount值都為2,

//盡管dog運行了release,但dog指針還是指向那個對象。

//此時dog對對象僅僅有使用權,而沒有擁有權。 NSLog(@"dog retainCount = %lu",[dog retainCount]); NSLog(@"dog1 retainCount = %lu",[dog1 retainCount]); NSLog(@"dog2 retainCount = %lu",[dog2 retainCount]); [dog1 release]; [dog2 release]; //運行完上面兩句話的時候,dog對象就銷毀了。 //盡管這裏我們能夠寫兩句[dog release]; //也能達到相同的效果。可是,務必不要這樣寫, //我們要遵守內存管理的黃金法則: Dog *dog = [[Dog alloc] init]; // 這是對dog指針進行alloc。須要相應[dog release]; Dog *dog1 = [dog retain]; //這是對dog1指針進行retain,須要相應[dog1 retain]; //這時候打印dog的retainCount是錯誤的使用方法!! //由於對象已經銷毀了!! 對一個已經銷毀的對象發送消息是邏輯錯誤的! //會造成程序的崩潰, //由於dog對象已經銷毀了。沒法調用dog對象的方法。 //註意。假設上面不加兩行打印的話,可能不會崩潰。 NSLog(@"dog retainCount = %lu",[dog retainCount]); } NSLog(@"程序即將退出"); return 0; }

3.3 類的復合中使用

在上面代碼中,添加Person類

@interface Person : NSObject {

  // 一個人。養了一條狗

    Dog *_dog;

  }

  - (void)setDog:(Dog *)dog;

  - (Dog *)dog;

  @end

setDog方法形式:

@implementation Person

  /* 人並沒有真正持有狗,

假設在main函數裏[dog release],讓dog的引用計數減1。就變為0,

dog就銷毀了。

    - (void)setDog:(Dog *)dog

  {

    _dog = dog;

  }

    */

 

  /* 假設人再持有別的狗。

就會造成第一條狗得不到釋放,內存泄露。

  - (void)setDog:(Dog *)dog

  {

    _dog = [dog retain];

  }

    */

 

  /* 假設本來持有一條狗,又又一次設置這條狗,先進行release,

這個時候。非常可能dog就銷毀了,然後,就沒法再次retain了。

- (void)setDog:(Dog *)dog { [_dog release]; _dog = [dog retain]; } */ // 標準寫法 - (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]; } @end

錯誤方法分析:

//第一個setDog:方法相應的錯誤

          Dog *xiaoBai = [[Dog alloc] init];

          Person *xiaoXin = [[Person alloc] init];

             [xiaoXin setDog:xiaoBai];

  //引用計數為1

             NSLog(@"count = %lu",xiaoBai.retainCount);

             [xiaoBai release];

  //此時狗已經銷毀了。因此,xiaoXin須要持有這條狗。

             [xiaoXin release];

 

 // 第二個setDog:方法相應的錯誤

               Dog *xiaoBai = [[Dog alloc] init];

                Person *xiaoXin = [[Person alloc] init];

                [xiaoXin setDog:xiaoBai];

  //引用計數為2

               NSLog(@"count = %lu",xiaoBai.retainCount);

               [xiaoBai release];

               Dog *xiaoHei = [[Dog alloc] init];

               [xiaoXin setDog:xiaoHei];

               [xiaoHei release];

               [xiaoXin release];

  //此時xiaoBai這條狗沒有釋放

 

  //第三個setDog:方法相應的錯誤

               Dog *xiaoBai = [[Dog alloc] init];

                Person *xiaoXin = [[Person alloc] init];

                [xiaoXin setDog:xiaoBai];

  //引用計數為2

               NSLog(@"count = %lu",xiaoBai.retainCount);

               [xiaoBai release];

  //這樣設置是不正確的,由於在setDog:裏。將dog進行release的時候,

  //引用計數為0,dog就銷毀了,無法再retain了。

[xiaoXin setDog:xiaoBai]; [xiaoXin release]; //另外,這裏還要說明,類裏。類外,都須要遵守內存管理。

3.4 @property retain,assign。copy展開

i.) retain展開

如上代碼裏。Person的setter和getter方法,也能夠用property,寫成例如以下形式:

@property (nonatomic, retain) Dog *dog;

進行如上測試,都沒有問題。

因此,[email protected] (nonatomic, retain) Dog *dog;,

則會展開例如以下:

- (void)setDog:(Dog *)dog

  {

    if (_dog != dog) {

             [_dog release];

             _dog = [dog retain];

      }

  }

 

  - (Dog *)dog

  {

    return _dog;

  }

 


ii.) assign展開

@property (nonatomic, assign) Dog *dog;,assign是直接復制,

則會展開例如以下:

- (void)setDog:(Dog *)dog

  {

         _dog = dog;

  }

  - (Dog *)dog

  {

     return _dog;

  }

iii.) copy展開

@property (nonatomic, copy) Dog *dog;。copy,拷貝。

將原來的對象拷貝一份出來,展開例如以下:

- (void)setDog:(Dog *)dog

  {

    if (_dog != dog) {

          [_dog release];

        _dog = [dog copy];

      }

  }

  - (Dog *)dog

  {

     return _dog;

  }

3.5 字符串內存管理

i.) 字符串的內存管理

對於字符串而言。很不遵守黃金法則! (假設從字符串的引用計數來看,亂七八糟!)這僅僅是一個表象! 事實上內部還是遵循的!!

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

NSString *str = [[NSString alloc] initWithFormat:@"%d %s",1,"hello"];

        NSLog(@"count1 = %lu",str.retainCount);

        NSString *str2 = @"hello";

         NSLog(@"count2 = %lu",str2.retainCount);

        NSString *str3 = [str retain];

        NSString *str4 = [str2 retain];

        NSLog(@"count3 = %lu",str.retainCount);

        NSLog(@"count4 = %lu",str2.retainCount);

        NSString *str5 = [[NSString alloc] initWithString:str];

        NSString *str6 = [[NSString alloc] initWithString:str2];

        NSLog(@"count5 = %lu",str5.retainCount);

        NSLog(@"count6 = %lu",str6.retainCount);

        // NSString *str7 = [NSString stringWithFormat:@"%d",5];

       [str release];

       [str3 release];

       [str4 release];

       [str5 release];

       [str6 release];

       // str7不用release!!

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

ii.) copy和mutableCopy

NSMutableString *string = [[NSMutableString alloc] initWithString:@"hello"];

 

        // 這樣寫會崩潰。看對象。不看指針

        // copy 將(可變或不可變)字符串拷貝成不可變字符串,string2 實際是不可變字符串

        // NSMutableString *string2 = [string copy];

 

        // [string2 appendString:@"world"];

        NSString *string2 = [string copy];

        NSLog(@"string2 = %@",string2);

 

        // mutableCopy 將(可變或不可變)字符串拷貝成可變字符串

        NSMutableString *string3 = [string2 mutableCopy];

        [string3 appendString:@"world"];

        NSLog(@"string3 = %@",string3);

 

        // 不用管它的引用計數是多少,我們遵守我們自己的黃金法則就夠了

        [string release];

        [string2 release];

        [string3 release];

        // UI裏,也不要隨便打印retainCount, 各人顧各人

        // new 相當於alloc init,在OC或IOS裏差點兒不用!!

3.6 數組的內存管理

int main(int argc, const char * argv[])

{

    @autoreleasepool {

 

        Dog *dog1 = [[Dog alloc] init];

        Dog *dog2 = [[Dog alloc] init];

        Dog *dog3 = [[Dog alloc] init];

        Dog *dog4 = [[Dog alloc] init];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        NSMutableArray *array = [NSMutableArray arrayWithObjects:dog1, 

dog2, dog3, dog4, nil];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

 

        [array addObject:dog1];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

 

        // NSLog(@"array = %@",array);

        [array removeLastObject];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

 

        // 新的array不是我們alloc new... 的。我們不須要release

        // [array release];

        NSLog(@"dog1.retainCount = %lu",dog1.retainCount);

        [dog1 release];

        [dog2 release];

        [dog3 release];

        [dog4 release];

 

    }

    //for @autoreleasepool

    return 0;

}

結論

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

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

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

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

總之,誰汙染誰治理,管好自己就能夠了。

3.7 autorelease與 autoreleasepool

autoreleasepool是一個對象

autorelease 是一個方法

在main函數裏寫例如以下代碼:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

      Dog *dog = [[Dog alloc] init];

//dog並沒有立即銷毀,而是延遲銷毀,

//將dog對象的擁有權交給了autoreleasepool

    [dog autorelease];

//這個是能夠打印的,由於打印完dog的引用計數後。

//dog對象才銷毀

@autoreleasepool{

     [dog autorelease]

}

    NSLog(@"retainCount = %lu",dog.retainCount);

     }

    NSLog(@"程序即將退出");

    return 0;

  }

註意: autoreleasepool相當於一個數組,假設哪個對象發送autorelease消息。實際將對象的擁有權交給了autoreleasepool;當autoreleasepool銷毀的時候,將向autoreleasepool裏持有的全部對象都發送一個release消息。

3.8 加方法的內存管理

我們用加方法創建的對象。不用我們release,是由於類內部的實現使用了autorelease。延遲釋放。

在Dog類的聲明裏添加一個加方法

+ (id)dog;

在Dog類的實現裏進行實現

+ (id)dog

{

/*註意,這裏不要寫成release。假設是release,那麽剛創建就銷毀了,

使用autorelease,使得將對象的擁有權交給了自己主動釋放池。

僅僅要自己主動釋放池沒有銷毀,dog對象也就不會銷毀。*/

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

}

在main函數裏代碼例如以下:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

             Dog *dog = [[[Dog alloc] init] autorelease];

             Dog *dog1 = [Dog dog];

             NSLog(@"count = %lu",dog.retainCount);

             NSLog(@"count1 = %lu",dog1.retainCount);

      }

  }

OC語言--內存管理