iOS開發-Day20-OC 手動內存管理

  • 爲什麼要進行內存管理。

    由於移動設備的內存極其有限,所以每個APP所佔的內存也是有限制的,當app所佔用的內存較多時,系統就會發出內存警告,這時需要回收一些不需要再繼續使用的內存空間,比如回收一些不再使用的對象和變量等。

    管理範圍:任何繼承NSObject的對象,對其他的基本數據類型無效。

    本質原因是因爲對象和其他數據類型在系統中的存儲空間不一樣,其它局部變量主要存放於棧中,而對象存儲於堆中,當代碼塊結束時這個代碼塊中涉及的所有局部變量會被回收,指向對象的指針也被回收,此時對象已經沒有指針指向,但依然存在於內存中,造成內存泄露。

  • 內存管理黃金法則:

    The basic rule to apple is everything thatincreases the reference counter with alloc,[mutable]copy[WithZone:] or retainis in charge of the corresponding [auto]release.

    如果一個對象使用了alloc,[mutable] copy,retain,那麼你必須使用相應的release或autonrelease

  • 對象的基本結構

    每個OC對象都有自己的引用計數器,是一個整數表示對象被引用的次數,即現在有多少東西在使用這個對象。對象剛被創建時,默認計數器值爲1,當計數器的值變爲0時,則對象銷燬。每個OC對象內部,都專門有4個字節的存儲空間來存儲引用計數器。

  • 引用計數器的作用

    判斷對象要不要回收的唯一依據就是計數器是否爲0,若不爲0則存在。

  • 操作

    給對象發送消息,進行相應的計數器操作。

    Retain消息:使計數器+1,改方法返回對象本身

    Release消息:使計數器-1(並不代表釋放對象)

    retainCount消息:獲得對象當前的引用計數器值

  • 對象的銷燬

    當一個對象的引用計數器爲0時,那麼它將被銷燬,其佔用的內存被系統回收。

    當對象被銷燬時,系統會自動向對象發送一條dealloc消息,一般會重寫dealloc方法,在這裏釋放相關的資源,dealloc就像是對象的“臨終遺言”。一旦重寫了dealloc方法就必須調用[super dealloc],並且放在代碼塊的最後調用(不能直接調用dealloc方法)。

    一旦對象被回收了,那麼他所佔據的存儲空間就不再可用,堅持使用會導致程序崩潰(野指針錯誤)。

  • 相關概念和使用注意

    野指針錯誤:訪問了一塊壞的內存(已經被回收的,不可用的內存)。

    殭屍對象:所佔內存已經被回收的對象,殭屍對象不能再被使用。(打開殭屍對象檢測)

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

    注意:不能使用[p retaion]讓殭屍對象起死復生。

  • 內存管理原則

    • 原則

      只要還有人在使用某個對象,那麼這個對象就不會被回收;

      只要你想使用這個對象,那麼就應該讓這個對象的引用計數器+1;

      當你不想使用這個對象時,應該讓對象的引用計數器-1;

    • 誰創建,誰release

      如果你通過alloc,new,copy來創建了一個對象,那麼你就必須調用release或者autorelease方法

      不是你創建的就不用你去負責

    • 誰retain,誰release

      只要你調用了retain,無論這個對象時如何生成的,你都要調用release

    • 總結

      有始有終,有加就應該有減。曾經讓某個對象計數器加1,就應該讓其在最後-1.

代碼規範:

1、只要調用了alloc,就必須有release(autorelease)
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方法的代碼規範

//一定要[super dealloc],而且要放到最後

//對self(當前)所擁有的的其他對象做一次release操作

-(void)dealloc
{
    [_car release];
    [super dealloc];
}
  • @property的內存參數

Retain(strong):對對象release舊值,retain新值(適用於OC對象類型)
Assign(week):直接賦值(默認,適用於非oc對象類型)
Copy:release舊值,copy新值
  • 內存管理中的循環引用問題以及解決

    案例:每個人有一張身份證,每張身份證對應一個人,不能使用#import的方式相互包含,這就形成了循環引用。
    新的關鍵字:@class 類名;——解決循環引用問題,提高性能
    @class僅僅告訴編譯器,在進行編譯的時候把後面的名字作爲一個類來處理。
    (1)@class的作用:聲明一個類,告訴編譯器某個名稱是一個類
    (2)開發中引用一個類的規範

        1)在.h文件中使用@class來聲明類
        2)在.m文件中真正要使用到的時候,使用#import來包含類中的所有東西
    

    (3)兩端循環引用的解決方法
    一端使用retain,一端使用assign(使用assign的在dealloc中也不用再release)

  • Autorelease

    (一)基本用法

    (1)會將對象放到一個自動釋放池中
    
    (2)當自動釋放池被銷燬時,會對池子裏的所有對象做一次release
    
    (3)會返回對象本身
    
    (4)調用完autorelease方法後,對象的計數器不受影響(銷燬時影響)
    

    (二)好處

    (1)不需要再關心對象釋放的時間
    
    (2)不需要再關心什麼時候調用release
    

    (三)使用注意

    (1)佔用內存較大的對象,不要隨便使用autorelease,應該使用release來精確控制
    
    (2)佔用內存較小的對象使用autorelease,沒有太大的影響
    

    (四)錯誤寫法

    (1)連續調用多次autorelease,釋放池銷燬時執行兩次release(-1嗎?)
    
    (2)Alloc之後調用了autorelease,之後又調用了release。
    

    (五)自動釋放池

    (1)在ios程序運行過程中,會創建無數個池子,這些池子都是以棧結構(先進後出)存在的。
    
    (2)當一個對象調用autorelease時,會將這個對象放到位於棧頂的釋放池中
    

下面的例子進一步詳解了手動內存管理:

//Car.h
#import <Foundation/Foundation.h>
#import "Engine.h"
@interface Car : NSObject
{
    //成員變量
    Engine *_eng;
}
-(void)setEng:(Engine *)eng;
-(Engine *)eng;
-(void)run;
@end
#import "Car.h"
//Car.m
@implementation Car
//第一次
//-(void)setEng:(Engine *)eng{
//    
//    //對於傳入eng,引用計數+1
//    _eng=[eng retain];
//    //_eng=eng;
//}
//第二次
//-(void)setEng:(Engine *)eng{
//    //如果不加release 內存泄露
//    [_eng release];
//    //對於傳入eng,引用計數+1
//    _eng=[eng retain];
//    //_eng=eng;
//}
//第三次
//-(void)setEng:(Engine *)eng{
//    
//    if (eng!=_eng) {
//        //如果不加release 內存泄露
//        [_eng release];
//        //對於傳入eng,引用計數+1
//        _eng=[eng retain];
//        //_eng=eng;
//    }
//    
//}
//簡便寫法
-(void)setEng:(Engine *)eng{

    [eng retain];
    [_eng release];
    _eng=eng;

}

-(Engine *)eng{
    return _eng;
}
-(void)run{
    NSLog(@"run");
}
- (void)dealloc
{
    //根據誰擁有誰釋放的原則,再次釋放對象
    NSLog(@"car銷燬");
    [_eng release];
    [super dealloc];
}
@end
//Engine.h
#import <Foundation/Foundation.h>
@interface Engine : NSObject
@end
//Engine.m
#import "Engine.h"
@implementation Engine
- (void)dealloc
{
    NSLog(@"eng銷燬");
    [super dealloc];
}
@end

//main.m
#import <Foundation/Foundation.h>
#import "test.h"
#import "Car.h"
int main(int argc, const char * argv[]) {
    @autoreleasepool {
//        //第一次測試
//        Car *car=[Car new];
//        Engine *eng=[Engine new];
//        //實際需要計數引用+1
//        car.eng=eng;
//        //eng釋放
//        [eng release];
//        //car釋放
//        [car release];

//        //第二次測試
//        Car *car=[Car new];
//        Engine *e1=[Engine new];
//        car.eng=e1;
//        [e1 release];//e1不會被釋放
//        
//        Engine *e2=[Engine new];
//        car.eng=e2;
//        [e2 release];
//        [car release];

        //第三次測試
        Car *car=[Car new];

        Engine *e=[Engine new];
        car.eng=e;
        [e release];

        car.eng=e;

        [car release];


    }
    return 0;
}

-15.8.7
-15.8.12

發佈了38 篇原創文章 · 獲贊 1 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章