11-Objective-C特有語法:內存管理總結

1、    基本原理

1.        什麼是內存管理

  • 移動設備的內存極其有限,每一個app所能佔用的內存是有限制的
  • 當app所佔用的內存較多時,系統會發出內存警告,這時得回收一些不須要再使用的內存空間。好比回收一些不須要使用的對象、變量等
  • 管理範圍:任何繼承了NSObject的對象,對其餘基本數據類型(int、char、float、double、struct、enum等)無效

 


 

2.        對象的基本結構

 

  • 每一個OC對象都有本身的引用計數器,是一個整數,表示「對象被引用的次數」,即有多少人正在使用這個OC對象
  • 每一個OC對象內部專門有4個字節的存儲空間來存儲引用計數器

 


 

3.        引用計數器的做用

  • 當使用alloc、new或者copy建立一個新對象時,新對象的引用計數器默認就是1
  • 當一個對象的引用計數器值爲0時,對象佔用的內存就會被系統回收。換句話說,若是對象的計數器不爲0,那麼在整個程序運行過程,它佔用的內存就不可能被回收,除非整個程序已經退出

 

4.        引用計數器的操做 

   1.方法的基本使用多線程

   1> retain :計數器+1,會返回對象自己app

   2> release :計數器-1,沒有返回值性能

   3> retainCount :獲取當前的計數器atom

   4> deallocspa

    * 當一個對象要被回收的時候,就會調用線程

    * 必定要調用[super dealloc],這句調用要放在最後面 3d

   2.概念指針

   1> 殭屍對象 :所佔用內存已經被回收的對象,殭屍對象不能再使用調試

   2> 野指針 :指向殭屍對象(不可用內存)的指針,給野指針發送消息會報錯(EXC_BAD_ACCESS)代碼規範

   3> 空指針 :沒有指向任何東西的指針(存儲的東西是nil、NULL、0),給空指針發送消息不會報錯

 

   3.原則

     1>.你想使用(佔用)某個對象,就應該讓對象的計數器+1(讓對象作一次retain操做)

       2>.你不想再使用(佔用)某個對象,就應該讓對象的計數器-1(讓對象作一次release)

       3>.誰retain,誰release

       4>.誰alloc,誰release


 

5.        對象的銷燬

  • 當一個對象的引用計數器值爲0時,那麼它將被銷燬,其佔用的內存被系統回收
  • 一旦對象被回收了,它佔用的內存就再也不可用,堅持使用會致使程序崩潰(野指針錯誤)
  • 當一個對象被銷燬時,系統會自動向對象發送一條dealloc消息
  • 通常會重寫dealloc方法,在這裏釋放相關資源,dealloc就像對象的遺言
  • 一旦重寫了dealloc方法,就必須調用[super dealloc],而且放在最後面調用
  • 不要直接調用dealloc方法

 

 6.內存管理代碼規範

 1.只要調用了alloc,必須有release(autorelease)

   對象不是經過alloc產生的,就不須要release

 

 2.set方法的代碼規範

 

1> 基本數據類型:直接複製
 - (void)setAge:(int)age
 {
    _age = age;
 }

 2> OC對象類型
 - (void)setCar:(Car *)car
 {
    // 1.先判斷是否是新傳進來對象
    if ( car != _car )
    {
        // 2.對舊對象作一次release
        [_car release]; 
        // 3.對新對象作一次retain
        _car = [car retain];
    }
 }

 3.dealloc方法的代碼規範 

1> 必定要[super dealloc],並且放到最後面
2> 對self(當前)所擁有的其餘對象作一次release
 - (void)dealloc
 {
    [_car release];
    [super dealloc];
 }

 


 

2、    Xcode的設置

1.     取消ARC

要想手動調用retain、release等方法,在建立項目的時候不要勾選ARC

 

 

2.     開啓殭屍對象監控

默認狀況下,Xcode是不會管殭屍對象的,使用一塊被釋放的內存也不會報錯。爲了方便調試,應該開啓殭屍對象監控

        

3、    內存管理原則

1.        原則分析

  • QQ堂開房間原理:只要房間還有人在用,就不會解散

 

  • 只要還有人在用某個對象,那麼這個對象就不會被回收
  • 只要你想用這個對象,就讓對象的計數器+1
  • 當你再也不使用這個對象時,就讓對象的計數器-1

2.        誰建立,誰release

  • 若是你經過alloc、new或[mutable]copy來建立一個對象,那麼你必須調用release或autorelease
  • 換句話說,不是你建立的,就不用你去[auto]release

3.        誰retain,誰release

  • 只要你調用了retain,不管這個對象是如何生成的,你都要調用release

4.        總結

  • 善始善終,有加就有減
  • 曾經讓對象的計數器+1,就必須在最後讓對象計數器-1

 


 

4、    @property參數

retain : 生成的set方法裏面,release舊值,retain新值

@property (retain) Book *book;

@property (retain) NSString *name;

 1.set方法內存管理相關的參數

 * retain : release舊值,retain新值(適用於OC對象類型)

 * assign : 直接賦值,不作任何內存管理(默認,適用於非OC對象類型)

 * copy   : release舊值,copy新值通常用於NSString *)

 2.是否要生成set方法

 * readwrite : 同時生成setter和getter的聲明、實現(默認)

 * readonly  : 只會生成getter的聲明、實現

 3.多線程管理

 * nonatomic : 性能高 (通常就用這個)

 * atomic    : 性能低(默認)

 4.setter和getter方法的名稱

 * setter : 決定了set方法的名稱,必定要有個冒號 :

 * getter : 決定了get方法的名稱(通常用在BOOL類型)

// 返回BOOL類型的方法名通常以is開頭

@property (getter = isRich) BOOL rich;  

@property (nonatomic, assign, readwrite) int weight;

@property (readwrite, assign) int height;

@property (nonatomic, assign) int age;

@property (retain) NSString *name;

 

 


 

5、  循環引用

1.        @class

  • 使用場景

對於循環依賴關係來講,比方A類引用B類,同時B類也引用A類,這種代碼編譯會報錯。當使用@class在兩個類相互聲明,就不會出現編譯報錯

  • 用法歸納

在開發中引用一個類的規範:

  在.h文件中用@class來聲明類,

  在.m實現文件中使用#import來包含類的全部內容

#import "Card.h"
// @class僅僅是告訴編譯器,Card是一個類
//@class Card;
  • 和#import的區別

*   #import方式會包含被引用類的全部信息,包括被引用類的變量和方法;@class方式只是告訴編譯器在A.h文件中 B *b 只是類的聲明,具體這個類裏有什麼信息,這裏不須要知道,等實現文件中真正要用到時,纔會真正去查看B類中信息

*   若是有上百個頭文件都#import了同一個文件,或者這些文件依次被#improt,那麼一旦最開始的頭文件稍有改動,後面引用到這個文件的全部類都須要從新編譯一遍,這樣的效率也是可想而知的,而相對來 講,使用@class方式就不會出現這種問題了

*   在.m實現文件中,若是須要引用到被引用類的實體變量或者方法時,還須要使用#import方式引入被引用類

2.        循環retain

  • 好比A對象retain了B對象,B對象retain了A對象
  • 這樣會致使A對象和B對象永遠沒法釋放

3.        解決方案

  • 當兩端互相引用時,應該一端用retain、一端用assign

 


 

6、  autorelease

1.autorelease的基本用法
 1> 給某個對象發送一條autorelease消息時,會將對象放到一個自動釋放池中
 2> 當自動釋放池被銷燬時,會對池子裏面的全部對象作一次release操做
 3> 會返回對象自己
 4> 調用完autorelease方法後,對象的計數器不變
 
 2.autorelease的好處
 1> 不用再關心對象釋放的時間
 2> 不用再關心何時調用release
 
 3.autorelease的使用注意
 1> 佔用內存較大的對象不要隨便使用autorelease
 2> 佔用內存較小的對象使用autorelease,沒有太大影響 
 
 4.錯誤寫法
 1> alloc以後調用了autorelease,又調用release
 @autoreleasepool
 {
    // 1
    Person *p = [[[Person alloc] init] autorelease]; 
    // 0
    [p release];
 }
 
 2> 連續調用屢次autorelease
 @autoreleasepool
 {
    Person *p = [[[[Person alloc] init] autorelease] autorelease];
 }
 
 5.自動釋放池
 1> 在iOS程序運行過程當中,會建立無數個池子。這些池子都是以棧結構存在(先進後出)
 2> 當一個對象調用autorelease方法時,會將這個對象放到棧頂的釋放池
 
 
 6.自動釋放池的建立方式
 1> iOS 5.0前
 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 [pool release]; // [pool drain];
 
 
 2> iOS 5.0 開始
 @autoreleasepool
 {
    
 }

void test() { @autoreleasepool {// { 開始表明建立了釋放池 // autorelease方法會返回對象自己 // 調用完autorelease方法後,對象的計數器不變 // autorelease會將對象放到一個自動釋放池中 // 當自動釋放池被銷燬時,會對池子裏面的全部對象作一次release操做 Person *p = [[[Person alloc] init] autorelease]; p.age = 10; @autoreleasepool { Person *p2 = [[[Person alloc] init] autorelease]; p2.age = 10; } Person *p3 = [[[Person alloc] init] autorelease]; } // } 結束表明銷燬釋放池 }
 

3.  應用實例 

  • 跟release的對比

    之前:

Book *book = [[Book alloc] init];
[book release];

    如今:

Book *book = [[[Book alloc] init] autorelease];
// 不要再調用[book release]; 

 1.系統自帶的方法裏面沒有包含alloc、new、copy,說明返回的對象都是autorelease的

 

 2.開發中常常會提供一些類方法,快速建立一個已經autorelease過的對象

 1> 建立對象時不要直接用類名,通常用self

+ (id)book {
    return [[[self alloc] init] autorelease];
}

  3.外界調用[Book book]時,根本不用考慮在何時釋放返回的Book對象

NSNumber *n = [NSNumber numberWithInt:100];
NSString *s = [NSString stringWithFormat:@"jack"];
NSString *s2 = @"rose";
相關文章
相關標籤/搜索