OC - OC的內(nèi)存管理機(jī)制

導(dǎo)讀

  • 一、為什么要進(jìn)行內(nèi)存管理
  • 二、內(nèi)存管理機(jī)制
  • 三、內(nèi)存管理原則
  • 四、MRC手動內(nèi)存管理
  • 五、ARC自動內(nèi)存管理
  • 六、Autorelease自動釋放池
  • 參考:http://m.itdecent.cn/p/7903c8283e26

一、 為什么要進(jìn)行內(nèi)存管理

  1. 移動設(shè)備分配給每個App的內(nèi)存有限,App運行中會創(chuàng)建大量對象, OC對象存儲在堆中,系統(tǒng)不會自動釋放堆中的內(nèi)存,對象沒有及時釋放,就會占用大量內(nèi)存,系統(tǒng)會發(fā)出內(nèi)存警告,對應(yīng)用運行造成影響。
  • 相關(guān)概念:
    內(nèi)存泄露:代碼塊結(jié)束時其內(nèi)部的所有局部變量會被回收,指向?qū)ο蟮闹羔樢脖换厥?,如果對象沒有指針指向,但依然存在于內(nèi)存中,造成內(nèi)存泄露。
    野指針錯誤:訪問了一塊壞的內(nèi)存(已經(jīng)被回收的,不可用的內(nèi)存)。
    僵尸對象:所占內(nèi)存已經(jīng)被回收的對象,僵尸對象不能再被使用。(打開僵尸對象檢測)
    空指針:沒有指向任何東西的指針(存儲的東西是0、null、 nil),給空指針發(fā)送消息不會報錯。

二、內(nèi)存管理機(jī)制

  1. 在C#中都有GC在自動管理內(nèi)存,但是在OC中沒有垃圾回收機(jī)制,OC提供了一套機(jī)制來管理內(nèi)存,即“引用計數(shù)”(retain counting):
  • 每一個對象都有一個引用計數(shù)(retain count),對象被創(chuàng)建的時候,引用計數(shù)的值是1 當(dāng)調(diào)用對象的alloc、retain、new、copy方法之后引用計數(shù)器值自動在原來的基礎(chǔ)上加1,當(dāng)調(diào)用對象的release方法之后引用計數(shù)器值減1,調(diào)用autorelease 使對象的引用計數(shù)在未來的某個時候減1, 當(dāng)引用計數(shù)值是0的時候,對象將被銷毀。
  • 在每個OC對象內(nèi)部,都專門有4個字節(jié)的存儲空間來存儲引用計數(shù)器。
內(nèi)存管理范圍:任何繼承NSObject的對象;OC中的基本類型存儲在棧上,由系統(tǒng)進(jìn)行管理。 
內(nèi)存管理方法有:MRC(手動引用計數(shù))、ARC(自動引用計數(shù))、Autorelease(自動釋放池)。

三、內(nèi)存管理原則

  1. 誰創(chuàng)建, 誰release。
    如果你通過alloc、new、copy來創(chuàng)建了一個對象,那么你就必須調(diào)用release或者autorelease方法。不是你創(chuàng)建的就不用你去負(fù)責(zé)。
  2. 誰retain, 誰release。
    只要你調(diào)用了retain,無論這個對象時如何生成的,你都要調(diào)用release。
  3. 除了alloc、new或copy之外的方法創(chuàng)建的對象在內(nèi)部都被聲明了autorelease,如[UIButton buttonWithType:]
  4. 在一定的代碼段內(nèi),對同一個對象所做的copy、alloc和retain的操作次數(shù)應(yīng)當(dāng)與release和 autorelease操作的次數(shù)相等。
    Apple官網(wǎng)內(nèi)存管理三定律
    1. 一個對象可以有一個或多個擁有者
    2. 當(dāng)對象一個擁有者都沒有時,就會被回收
    3. 一個對象如果想保留不被回收,必須具有擁有者

四、MRC手動引用計數(shù)

  • 在引入ARC(Automatic Reference Counting)自動引用計數(shù)機(jī)制之前,OC的內(nèi)存管理需要由開發(fā)人員手動維護(hù)即MRC。
  • 在Xcode4.2之后的版本中??引入了ARC,程序編譯時,Xcode可以自動為你的代碼添加內(nèi)存釋放代碼,如果編寫手動釋放代碼Xcode會報錯,因此如果使用的Xcode4.2之后的版本,必須手動關(guān)閉ARC,這樣才有助于我們理解OC的內(nèi)存管理機(jī)制。
  • 為了理解OC的內(nèi)存管理機(jī)制,需要在Xcode中關(guān)閉ARC:項目屬性—Build Settings--搜索“garbage”找到Objective-C Automatic Reference Counting設(shè)置為No即可。


(一) Set方法的代碼規(guī)范
  1. 基本數(shù)據(jù)類型:直接復(fù)制
    -(void)setAge:(int)age
    {
    _age=age;
    }
  2. OC對象類型
    -(void)setCar:(Car *)car
    {
    //先判斷是不是新傳進(jìn)來的對象
    If(car!=_car)
    {
    //對舊對象做一次release
    [_car release];//若沒有舊對象,則沒有影響
    //對新對象做一次retain
    _car=[car retain];
    }
    }
(二) dealloc方法的代碼規(guī)范
  1. 一定要[super dealloc],而且要放到最后
  2. 對self(當(dāng)前)所擁有的的其他對象做一次release操作
    -(void)dealloc
    {
    [_car release];
    [super dealloc];
    }
(三) 實例代碼規(guī)范
  1. WZKPerson.h
#import <Foundation/Foundation.h>
@interface WZKPerson : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
@end
  • WZKPerson.m
    #import "WZKPerson.h"
    @implementation WZKPerson
    -(void)dealloc
    {
    self.name=nil;
    /*最后一定要調(diào)用父類的dealloc方法;
    目的:一是父類可能有其他引用對象需要釋放;二是當(dāng)前對象真正的釋放操作是在super的dealloc中完成的;
    */
    [super dealloc];
    }
    @end

  • main.m(部分代碼)
    //調(diào)用alloc,引用計數(shù)+1
    WZKPerson *personTest=[[WZKPerson alloc] init];
    personTest.name=@"test";
    personTest.age=30;

    //輸出personTest對象的引用計數(shù)
    NSLog(@"personTest的引用計數(shù):%lu",[personTest retainCount]);
    //輸出結(jié)果:personTest的引用計數(shù):1
    
    //執(zhí)行personTest的dealloc方法
    //調(diào)用過release方法之后,personTest指向的對象就會被銷毀,但是此時變量personTest中還存放著WZKPerson對象的地址
    [personTest release];
    
    //如果不設(shè)置personTest=nil,則personTest就是一個野指針,它指向的內(nèi)存不屬于這個程序,非常危險
    personTest=nil;
    
    //如果不設(shè)置personTest=nil,此時再調(diào)用personTest的release方法會報錯
    //如果設(shè)置了personTest=nil,此時personTest已經(jīng)是空指針了,則oc中給空指針發(fā)送消息是不會報錯的
    [personTest release];
    
    WZKPerson *personTest2=[[WZKPerson alloc] init];
    personTest2.name=@"test2";
    personTest2.age=30;
    
    //輸出結(jié)果:personTest的引用計數(shù):1
    NSLog(@"personTest2的引用計數(shù):%lu",[personTest2 retainCount]);
    
    //引用計數(shù)+1
    [personTest2 retain];
    //輸出結(jié)果:personTest的引用計數(shù):2
    NSLog(@"personTest2的引用計數(shù):%lu",[personTest2 retainCount]);
    
    //引用計數(shù)-1
    [personTest2 release];
    //輸出結(jié)果:personTest的引用計數(shù):1
    NSLog(@"personTest2的引用計數(shù):%lu",[personTest2 retainCount]);
    
    //執(zhí)行personTest2的dealloc方法
    [personTest2 release];
    
    personTest2=nil;
    
  • 在上述代碼中,可以通過dealloc方法來查看是否一個對象已經(jīng)被回收,如果沒有回收,則有可能造成內(nèi)存泄漏。
    如果一個對象被釋放后,那么最后引用它的變量需要手動設(shè)置為nil,否則可能造成野指針錯誤。

五、ARC內(nèi)存管理機(jī)制

(一) ARC的判斷準(zhǔn)則
  • 只要沒有強指針指向,對象就會被釋放。
(二) 指針分類
  • 強指針:默認(rèn)的情況下,所有的指針都是強指針,關(guān)鍵字strong
  • 弱指針:_ _weak關(guān)鍵字修飾的指針
  1. 聲明一個弱指針如下:
    _ _weak Person *p;
    //ARC中,只要弱指針指向的對象不在了,就直接把弱指針做清空操作。
    _ _weak Person *p=[[Person alloc] init];
    //不合理,對象一創(chuàng)建出來就被釋放掉,對象釋放掉后,ARC把指針自動清零。
  2. ARC中在property處不再使用retain,而是使用strong,在dealloc中不需要再[super dealloc]
    @property(nonatomic,strong)Dog *dog;
    // 意味著生成的成員變量dog是一個強指針,相當(dāng)于以前的retain。
    //如果換成是弱指針,則換成weak,不需要加
    _。
(三) ARC的特點總結(jié)
  1. 不允許調(diào)用release、retain、retainCount
  • 允許重寫dealloc,但是不允許調(diào)用[super dealloc]
  • @property的參數(shù):
 Strong:相當(dāng)于原來的retain(適用于OC對象類型),成員變量是強指針
Weak:相當(dāng)于原來的assign(適用于oc對象類型),成員變量是弱指針
Assign:適用于非OC對象類型(基礎(chǔ)類型)
(四) 補充
  1. 讓程序兼容ARC和非ARC部分。轉(zhuǎn)變?yōu)榉茿RC -fno-objc-arc 轉(zhuǎn)變?yōu)锳RC的, -f-objc-arc 。
  • ARC也需要考慮循環(huán)引用問題:一端使用retain,另一端使用assign。


  • 提示:字符串是特殊的對象,但不需要使用release手動釋放,這種字符串對象默認(rèn)就是autorelease的,不用額外的去管內(nèi)存。

六、 Autorelease自動釋放池

  • 在OC中存在著一種內(nèi)存自動釋放機(jī)制叫做自動釋放池(或自動引用計數(shù)),但是與C#不同的是,這僅僅是一種半自動的機(jī)制,有些操作還是需要進(jìn)行手動設(shè)置。
(一) 下面通過代碼來了解一下自動釋放池
  1. WZKPerson.h
    //構(gòu)造函數(shù)
    -(WZKPerson *)initWithName:(NSString *)name age:(NSInteger)age;
    //獲取對象的類方法
    +(WZKPerson *)personWithName:(NSString *)name;
  • WZKPerson.m
    -(WZKPerson *)initWithName:(NSString *)name age:(NSInteger)age
    {
    self=[super init];
    if (self) {
    _name=[name copy];
    _age=age;
    }
    return self;
    }

    +(WZKPerson *)personWithName:(NSString *)name
    {
        //這里調(diào)用了autorelease
        //OC類庫中的類方法一般都不需要手動釋放,內(nèi)部已經(jīng)調(diào)用了autorelease方法;
        WZKPerson *person=[[[WZKPerson alloc] init] autorelease];
        return person;
    }
    
  • main.m(部分代碼)
    int main(int argc, const char * argv[]) {
    @autoreleasepool {
    WZKPerson *person1=[[WZKPerson alloc] init];
    //調(diào)用autorelease方法,后面就不需要手動調(diào)用release方法了
    [person1 autorelease];
    //由于autorelease是延遲釋放(延遲到自動釋放池銷毀),

      //所以這里仍然可以使用person1對象
      person1.name=@"Kevin";
    
      //調(diào)用autorelease方法
      WZKPerson *person2=[[[WZKPerson alloc] initWithName:@"Kevin" age:27] autorelease];
    
      //內(nèi)部已經(jīng)調(diào)用了autorelease,所以不需要手動釋放
      //另外由于內(nèi)存管理原則,在外部不使用alloc、new、copy操作,
      //就不需要調(diào)用release或autorelease,所以這個操作是放到類方法內(nèi)部進(jìn)行完成
      WZKPerson *person3=[WZKPerson personWithName:@"Kevin"];
        }
        return 0;
    }
    
(二) 自動內(nèi)存釋放總結(jié)
1. 基本用法
  • 自動內(nèi)存釋放使用@autoreleasepool關(guān)鍵字聲明一個代碼塊,如果一個對象在初始化時調(diào)用了autorelease方法,會將這個對象放到位于棧頂?shù)尼尫懦刂小?/li>
  • 當(dāng)代碼塊執(zhí)行完之后即當(dāng)自動釋放池被銷毀時,在塊中調(diào)用過autorelease方法的對象都會自動調(diào)用一次release方法, 但是不一定能夠銷毀對象,例如:當(dāng)對象引用計數(shù)器值大于1時,該對象就無法銷毀。
  • OC中類庫的類方法一般都不需要手動釋放,因為內(nèi)部已經(jīng)調(diào)用了autorelease方法。
2. 好處
  • 不需要再關(guān)心對象釋放的時間
  • 不需要再關(guān)心什么時候調(diào)用release
3. 使用注意
  • 由于自動釋放池最后統(tǒng)一銷毀對象,因此如果一個操作比較占用內(nèi)存,最好不要放到自動釋放池或者放到多個自動釋放池;應(yīng)該使用release來精確控制
  • 占用內(nèi)存較小的對象使用autorelease,沒有太大的影響。
  • autorelease方法不會改變對象的引用計數(shù)器(銷毀時影響),只是將這個對象放到自動釋放池中;
  • 系統(tǒng)自帶的方法中,如果不包含alloc、 new 、copy等,則這些方法返回的對象都是autorelease的,如[NSDate date]。
  • 開發(fā)中經(jīng)常會寫一些類方法來快速創(chuàng)建一個autorelease對象,創(chuàng)建對象時不要直接使用類名,而是使用self。
4. 錯誤寫法
  • 連續(xù)調(diào)用多次autorelease,釋放池銷毀時執(zhí)行兩次release(-1嗎?)
  • Alloc之后調(diào)用了autorelease,之后又調(diào)用了release。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 29.理解引用計數(shù) Objective-C語言使用引用計數(shù)來管理內(nèi)存,也就是說,每個對象都有個可以遞增或遞減的計數(shù)...
    Code_Ninja閱讀 1,756評論 1 3
  • 內(nèi)存管理 簡述OC中內(nèi)存管理機(jī)制。與retain配對使用的方法是dealloc還是release,為什么?需要與a...
    丶逐漸閱讀 2,086評論 1 16
  • iOS內(nèi)存管理 概述 什么是內(nèi)存管理 應(yīng)用程序內(nèi)存管理是在程序運行時分配內(nèi)存(比如創(chuàng)建一個對象,會增加內(nèi)存占用)與...
    蚊香醬閱讀 5,825評論 8 119
  • 內(nèi)存管理是程序在運行時分配內(nèi)存、使用內(nèi)存,并在程序完成時釋放內(nèi)存的過程。在Objective-C中,也被看作是在眾...
    蹲瓜閱讀 3,392評論 1 8
  • 正確的臉部按摩手法,可以說是瘦臉最快最直接的方法了,不僅可以讓你逐漸瘦出巴掌臉,還能使你的臉部肌膚變得年輕緊致。每...
    你咋不下地閱讀 558評論 0 4

友情鏈接更多精彩內(nèi)容