天天看點

【黑馬程式員】-oc中的記憶體管理

------- <ahref="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">android教育訓練</a>、<ahref="http://www.itheima.com" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="blank">java教育訓練</a>、期待與您交流! ----------

1. 什麼是記憶體管理?

       由于我們程式運作的任何裝置的記憶體都是有限的,是以合理的安排記憶體,處理好記憶體的問題,是一個非常重要的技術。當一個對象已經不再使用,例如植物大戰僵屍中的子彈,當它飛出螢幕時,我們就要及時的讓它釋放占用的記憶體。這樣就為我們提高了記憶體的利用效率。如果一個APP不能及時的釋放廢棄的空間時,當它占用較大的記憶體是,系統就會發出警告,這是就要及時的處理掉“垃圾”。

       資料或者對象一般會存放到堆或者棧中。棧中存放内容一般都會自動釋放,而棧中的内容就需要我們去手動釋放。

       在學習這部分的内容時,程式設計中要關閉Xcode中的ARC機制。在Xcode6.2版本中,建立項目是并不會讓你去選擇是否帶ARC,而要在建立好項目之後進行取消。具體方法是:首先建立好項目,然後點項目名,在Building Setting中可以找到Apple LLVM 6.0 Language-Object c,在此架構下你可以看到Object c Automatic Reference Counting,選擇NO 就可以了。

2. 引用計數器

     每個oc對象都有一個引用計數器。引用計數器用來判斷此對象在什麼時候釋放記憶體。當某個oc對象的引用計數器為0時,那麼oc對象占用的記憶體将會被釋放。每個引用計數器都占用4個位元組的存儲空間。

3. 記憶體管理常用的關鍵字

     (1)alloc:用來建立一個對象空間。一般它都會配合init的使用,比如:[[NSObject alloc] init],建立一個類型為NSObject的對象,并對其進行初始化。

     (2)release:給對象發送一個release消息,對象的引用計數器減1。一般配合retain的使用,一個retain就有一個release。

     (3)retain:給對象發送一個retain消息,對象的引用計數器加1。

     (4)retainCount:擷取對象目前的引用計數器數值,傳回值類型為長整型。

     (5)autorelease:會将調用的對象放到一個自動釋放池中。當釋放池結束時,對象會自動調用一次release,引用計數器減1。在調用此指令是,對象引用計數器并不會立即減1。有傳回值,傳回值類型為對象本身。而且autorelease必須放到自動釋放池(釋放池是以棧的形式存放,先進後出)中使用。例如:

//自動釋放池
    @autoreleasepool {
        User *suer =[[[User alloc] init] autorelease];
    } //自動釋放池結束。
           

它的缺點是不能精确的釋放,是以使用時占用記憶體較大的對象不要使用autorelease。

     (6)dealloc:相當于對象的“遺言”。在對象被銷毀時,對象會自動調用dealloc。而且在調用dealloc時,一定會有調用[super dealloc],而且一定要放到最後調用。

4. 幾個要注意的概念

    (1)空指針:沒有指向任何對象的指針,也就是說指針指向的内容為nil、null或者0。

    (2)僵屍對象:所占用記憶體已經被回收的對象,而且僵屍對象不能再使用。

    (3)野指針:指向僵屍對象的指針。

      利用下邊的程式來看一下這些概念:

#import <Foundation/Foundation.h>

@interface Person : NSObject
@end

@implementation Person

- (void)dealloc
{
    NSLog(@"Person對象銷毀");
    
    [super dealloc];
}
@end

int main()
{
    //定義了一個空指針。
    int *p = nil;
    
    //定義一個Person對象,引用計數器為1。
    Person *p1 = [[Person alloc] init];
    
    //進行一次release,對象引用計數器變為0。此時,指針p1已經是野指針,因為對象已經銷毀。
    [p1 release];
    
    //将野指針指向空,變成空指針。
    p1 = nil;
    //對空指針進行release是不會報錯的。
    [p1 release];
    
    [p1 release];
    return 0;
}
           

     在出現野指針錯誤時,一般會報錯為:EXC-BAD-ACCESS。是以,需要牢記,下次遇見這樣的報錯就知道什麼原因了。

5. 記憶體管理的一些總結

(1)想要使用一個對象,就必須讓對象的引用計數器加1(也就是引用retain操作)。

(2)有加就有減。如果用alloc、new或者copy建立一個對象,必須調用release或者autorelease對對象進行一次引用計數器減1。

(3)不想使用對象,就應該讓對象的計數器-1,也就是讓對象進行一次release。

(4)誰retain,誰release;誰alloc,誰release。

6. set方法記憶體管理

        其實,在調用set方法時,如果有傳遞參數,而且傳遞參數為對象,就不得不考慮對象的引用計數器的加減。對于,基本資料類型參數,set設定方法為:

//基本資料類型,set方法直接指派
- (void)setAge
{
    _age = age;
}
           

但是對于對象,就要進行進一步的考慮:

//傳遞的參數為對象類型
- (void)setDog:(Dog *)dog
{
    //判斷時候為已經擁有的對象。防止出現重複指向時,使對象變為僵屍對象。
    if (self.dog != dog)
    {
        //要放棄的對象進行-1
        [_dog release];
        //新對象+1,然後指派給成員變量
        _dog = [dog retain];
    }
}
           

7. @property參數寫法

@property參數的類型主要分為4類,在進行@property參數書寫時,不同類型的參數可以同時存在,但相同類型的參數隻能存在一個。

(1)記憶體管理相關參數:

retain:表示進行對象的set方法,即set方法中的第二中方法;

assign:一般對于基本資料類型,進行直接指派操作。當然,在類的互相引用時,對已對象類型的參數,也要用assign。預設情況為此類型。

copy:對舊對象進行一次release,對新對象進行copy。

(2)是否生成set方法:

readonly:隻生成set方法,不生成聲明;

readwrite:可讀可寫,預設情況下為此方法。

(3)多線程管理

nonatomic:可以提高性能,以後寫程式時,必須加上這個。否則就是atomic。

atomic:性能低,而且預設情況下為此形式。

(4)set方法和get方法的名稱

getter = 方法名, setter = **:,不可漏了”:“。

如下是一個簡單的例子:

@property (nonatomic, retain) NSString *text;

@property (nonatomic, retain) NSString *peitu;

@property (nonatomic, assign) time_t time;

@property (nonatomic, retain) User *user;
           

8. 循環引用

         在循環引用時,不可能利用#import進行頭檔案的複制。是以,為了解決此問題,引入了@class。

         @class   ***;在.h檔案中僅僅是告訴編譯器,***是一個類。并沒有對其進行複制,是以在.m檔案中還要對其進行#import操作。利用@class可以提高程式性能,同時解決了循環引用的問題。

另外就是循環return的問題,在類與類之間的循環return會使對象不能夠釋放。解決方法是一端用retain,而另一端用assign。

9.ARC機制

         ARC是編譯器特性,不是垃圾回收。在編譯時,自動檢測哪裡需要插入release。

         ARC判斷标準:隻要沒有強指針指向對象,那麼對象就會釋放。

          指針分類:

       (1)強指針:預設情況下所有的指針都是強指針,聲明是利用__strong來聲明。

       (2)弱指針:不能決定對象是否釋放。定義時用__weak聲明。

繼續閱讀