OC基礎(六)——記憶體管理

weixin_33830216發表於2017-05-24

記憶體管理概述

  1. 記憶體管理
    記憶體的作用:儲存資料.
  • 如何將資料儲存到記憶體之中.
    宣告1個變數.然後將資料儲存進去.

  • 當資料不再被使用的時候,佔用的記憶體空間如何被釋放.

  1. 記憶體中的五大區域

    棧: 區域性變數. 當區域性變數的作用域被執行完畢之後,這個區域性變數就會被系統立即回收.
    堆: OC物件.使用C函式申請的空間.
    BSS段: 未初始化的全域性變數、靜態變數. 一旦初始化就回收 並轉存到資料段之中.
    資料段: 已經初始化的全域性變數、靜態變數. 直到程式結束的時候才會被回收.
    程式碼段: 程式碼. 程式結束的時候,系統會自動回收儲存在程式碼段中的資料.

    棧、BSS段、資料段、程式碼段儲存在它們中的資料的回收,是由系統自動完成的.不需要我們干預.

  2. 分配在堆區中的OC物件,是肯定需要被回收的.

    iPhone 記憶體機制.

    40M 警告
    45M 警告
    120M 閃退.

    儲存在堆中的OC物件,系統不會自動回收. 直到程式結束的時候才會被回收.

  3. 記憶體管理的範圍:
    只需要管理儲存在堆中的OC物件的回收.其他區域中的資料的回收是系統自動管理的.

  4. 物件應該什麼時候被回收?

    當有人使用這個物件的時候,這個物件就千萬不能回收.
    只有在沒有任何人使用這個物件的時候,才可以回收.

  5. 引用計數器

    1). 每1個物件都有1個屬性.叫做retainCount.叫做引用計數器. 型別是unsigned long 佔據8個位元組.
    引用計數器的作用: 用來記錄當前這個物件有多少個人在使用它.
    預設情況下,建立1個物件出來 這個物件的引用計數器的預設值是1.

    2). 當多1個人使用這個物件的時候.應該先讓這個物件的引用計數器的值+1 代表這個物件多1個人使用.

    3). 當這個物件少1個人使用的時候.應該先讓這個物件的引用計數器的值-1 代表這個物件少1個人使用.

    4). 當這個物件的引用計數器變為0的時候.代表這個物件無人使用. 這個時候系統就會自動回收這個物件.

  6. 如何操作引用計數器.

    1). 為物件傳送1條retain訊息. 物件的引用計數器就會加1. 當多1個人使用物件的時候才發.

    2). 為物件傳送1條release訊息.物件的引用計數器就會減1. 當少1個人使用物件的時候才發.

    3). 為物件傳送1條retainCount訊息. 就可以去到物件的引用計數器的值.

    就這樣++ -- 當物件的引用計數器變為0的時候,物件就會被系統立即回收.

    在物件被回收的時候.會自動呼叫物件的dealloc方法.

  7. 記憶體管理的分類

    MRC: Manual Reference Counting 手動引用計數.手動記憶體管理.

    當多1個人使用物件的時候,要求程式設計師手動的傳送retain訊息.少1個人使用的時候程式設計師手動的傳送relase訊息.

    2011年之前 iOS5之前
    ARC: Automatic Reference Counting 自動引用計數.自動記憶體管理.
    系統自動的在合適的地方傳送retain relase訊息.

第一個MRC程式(這個簡單知道就行了)

  1. iOS5開始. Xcode4.2開始就支援ARC
    Xcode7 預設支援ARC開發.
    預設使用的開發方式就是ARC的模式.

    關閉ARC開啟MRC.

  2. 當物件的引用計數器變為0的時候,系統會自動回收物件.
    在系統回收物件的時候.會自動的呼叫物件的dealloc方法.

    重寫dealloc方法的規範:
    必須要呼叫父類的dealloc方法. 並且要放在最後一句程式碼.

  3. 測試引用計數器.

    1). 新建立1個物件,這個物件的引用計數器的值預設是1.
    2). 當物件的引用計數器變為0的時候.物件就會被系統立即回收 並自動呼叫dealloc方法.
    3). 為物件傳送retain訊息 物件的引用計數器就會+1

  4. 為物件傳送release訊息.並不是回收物件.而是讓物件的引用計數器-1
    當物件的引用計數器的值變為0的時候.物件才會被系統立即回收.

記憶體管理原則

  1. 記憶體管理的重點

    1). 什麼時候為物件傳送retain訊息.

    當多1個人使用這個物件的時候,應該先為這個物件傳送retain訊息.

    2). 什麼時候為物件傳送release訊息.

    當少1個人使用這個物件的時候.應該為這個物件傳送1條release訊息.

  2. 在ARC機制下,retain release dealloc這些方法無法呼叫.

  3. 記憶體管理的原則

    1). 有物件的建立,就要匹配1個release

    2). retain的次數和release的次數要匹配.

    3). 誰用誰retain. 誰不用誰release.
    誰負責retain 誰就負責relase

    4). 只有在多1個人用的時候才retain 少1個人使用的時候才release

    有始有終,有加就有減. 有retain就應該匹配1個release 一定要平衡.

野指標與殭屍物件

  1. 野指標

    C語言中的野指標: 定義1個指標變數.沒有初始化.這個指標變數的值是1個垃圾值,指向1塊隨機的空間.這個指標就叫做野指標.

    OC中的野指標: 指標指向的物件已經被回收了.這樣的指標就叫做野指標.

  2. 物件回收的本質.

    記憶體回收的本質:
    申請1個變數,實際上就是向系統申請指定位元組數的空間.這些空間系統就不會再分配給別人了.
    當變數被回收的時候,代表變數佔用的位元組空間從此以後系統可以分配給別人使用了.
    但是位元組空間中儲存的資料還在.

    回收物件:
    所謂的物件的回收,指的是物件佔用的空間可以分配給別人.
    當這個物件佔用的空間沒有分配給別人之前 其實物件資料還在.

  3. 殭屍物件
    1個已經被釋放的物件,但是這個物件所佔的空間還沒有分配給別人.這樣的物件叫做殭屍物件.

    我們通過野指標去訪問殭屍物件的時候.有可能沒問題 也有可能有問題.

    當殭屍物件佔用的空間還沒有分配給別人的時候.這是可以的.
    當殭屍物件佔用的空間分配給了別人使用的時候 就不可以.

  4. 我們認為只要物件稱為了殭屍物件,無論如何 都不允許訪問了.
    就希望如果訪問的是殭屍物件,無論如何報錯.

    殭屍物件的實時檢查機制.可以將這個機制開啟. 開啟之後. 只要訪問的是殭屍物件,無論空間是否分配 就會報錯.

  5. 為什麼不預設開啟殭屍物件檢測.

    一旦開啟殭屍物件檢測 那麼在每訪問1個物件的時候 都會先檢查這個物件是否為1個殭屍物件,
    這樣是極其消耗效能的.

  6. 使用野指標訪問殭屍物件會報錯. 如何避免殭屍物件錯誤..

    當1個指標稱為野指標以後.將這個指標的值設定nil

    當1個指標的值為nil 通過這個指標去呼叫物件的方法(包括使用點語法)的時候.不會報錯. 只是沒有任何反應.
    但是如果通過直接訪問屬性 -> 就會報錯.

  7. 無法復活1個殭屍物件.

單個物件的記憶體管理

  1. 記憶體洩露.

    指的是1個物件沒有被及時的回收.在該回收的時候而沒有被回收
    一直駐留在記憶體中,直到程式結束的時候才回收.

  2. 單個物件的記憶體洩露的情況.

    1). 有物件的建立,而沒有對應的relase

    2). retain的次數和relase的次數不匹配.

    3). 在不適當的時候,為指標賦值為nil

    4). 在方法中為傳入的物件進行不適當的retain

  3. 如何保證單個物件可以被回收

    1). 有物件的建立 就必須要匹配1個relase

    2). retain次數和release次數一定要匹配.

    3). 只有在指標稱為野指標的時候才賦值為nil

    4). 在方法中不要隨意的為傳入的物件retain.

多個物件的記憶體管理

  1. 當屬性是1個OC物件的時候. setter方法的寫法.

    將傳進來的物件賦值給當前物件的屬性,代表傳入的物件多了1個人使用,所以我們應該先為這個傳入的物件傳送1條retain訊息 再賦值.

    噹噹前物件銷燬的時候.代表屬性指向的物件少1個人使用. 就應該在dealloc中relase

    程式碼寫法:

    - (void)setCar:(Car *)car
      {
      _car = [car retain];
      }
    
    - (void)dealloc
      {
      [_car release];
      [super dealloc];
      }
    
  2. 當屬性是1個OC物件的時候,setter方法照著上面那樣寫,其實還是有Bug的.

  3. 當為物件的這個屬性多次賦值的時候.就會發生記憶體洩露.
    發生洩露的原因: 當為屬性賦值的時候, 代表舊物件少1個人用.新物件多1個人使用.
    應該relase舊的 retain新的.

    -(void)setCar:(Car *)car
    
    {
    
    [_car release];
    
    _car = [car retain];
    
    }
    

@property引數

  1. 在MRC的開發模式下.1個類的屬性如果是1個OC物件型別的.那麼這個屬性的setter方法就應該按照下面的格式寫.

    - (void)setCar:(Car *)car
      {
      if(_car != car)
      {
      [_car release];
      _car = [car retain];
      }
      }
    
    還要重寫dealloc方法.
    
    - (void)dealloc
      {
      [_car release];
      [super delloc];
      }
    
    如果屬性的型別不是OC物件型別的.不需要像上面那樣寫. 還是像之前那樣寫就OK了.
    
  2. @property

    1). 作用
    a. 自動生成私有屬性.
    b. 自動生成這個屬性的getter setter方法的宣告.
    c. 自動生成這個屬性的getter setter方法的實現.

    特別播報:
    生成的setter方法的實現中,無論是什麼型別的,都是直接賦值.

  3. @property引數.

    1). @property可以帶引數的.

    @property(引數1,引數2,引數3......)資料型別 名稱;

    2). 介紹一下@property的四組引數.

    a. 與多執行緒相關的兩個引數.
    atomic、nonatomic.

    b. 與生成的setter方法的實現相關的引數.
    assign、retain.

    c. 與生成只讀、讀寫相關的引數
    readonly readwrite

    d. 是與生成的getter setter方法名字相關的引數.
    getter setter

  4. 介紹與多執行緒相關的引數.

    atomic: 預設值. 如果寫atomic,這個時候生成的setter方法的程式碼就會被加上一把執行緒安全鎖.
    特點: 安全、效率低下.
    nonatomic: 如果寫nonatomic 這個時候生成的setter方法的程式碼就不會加執行緒安全鎖.
    特點: 不安全,但是效率高.

    建議: 要效率. 選擇使用nonatomic 在沒有講解多執行緒的知識以前 統統使用nonatomic

  5. 與生成的setter方法的實現相關的引數.

    assign: 預設值 生成的setter方法的實現就是直接賦值.

    retain: 生成的setter方法的實現就是標準的MRC記憶體管理程式碼.
    也就是. 先判斷新舊物件是否為同1個物件 如果不是 release舊的 retain新的.

    當屬性的型別是OC物件型別的時候,那麼就使用retain
    當屬性的型別是非OC物件的時候,使用assign.

    千萬注意:
    retain引數.只是生成標準的setter方法為標準的MRC記憶體管理程式碼 不會自動的再dealloc中生成relase的程式碼.
    所以, 我們還要自己手動的在dealloc中release

  6. 與生成只讀、讀寫的封裝.

    readwrite: 預設值.代表同時生成getter setter
    readonly: 只會生成getter 不會生成setter

  7. 生成getter、setter方法名稱相關的引數.

    預設情況下.@property生成的getter setter方法的名字都是最標準的名字.
    其實我們可以通過引數來指定@property生成的方法的名字.

    getter = getter方法名字 用來指定@property生成的getter方法的名字.
    setter = setter方法名字.用來指定@property生成的setter方法的名字. 注意.setter方法是帶引數的 所以要加1個冒號.

    記住:如果使用getter setter修改了生成的方法的名字.
    在使用點語法的時候.編譯器會轉換為呼叫修改後的名字的程式碼.

    修改生成的getter setter方法名字. 因為預設情況下生成的方法的名字已經是最標準的名字了.
    所以.一般情況下不要去改.

1). 無論什麼情況都不要改setter方法的名字. 因為預設情況下生成的名字就已經是最標準的了.
2). 什麼時候修改getter方法的名字.當屬性的型別是1個BOOL型別的時候.就修改這個getter的名字以is開頭 提高程式碼的閱讀性.

------總結-------

  1. 與多執行緒相關的引數: 用nonatomic

  2. 與生成的setter方法實現相關的引數
    屬性的型別是OC物件的時候 使用retain
    屬性的型別是非OC物件的時候 使用assign

  3. 只讀 讀寫.
    如果你希望生成的封裝是隻讀封裝 那麼就使用readonly
    如果希望讀寫封裝 readwrite

  4. 1). 無論什麼情況都不要改setter方法的名字. 因為預設情況下生成的名字就已經是最標準的了.
    2). 什麼時候修改getter方法的名字.當屬性的型別是1個BOOL型別的時候.就修改這個getter的名字以is開頭 提高程式碼的閱讀性.

------使用引數注意-----

  1. 同1組引數只能使用1個.
    getter setter可以同時使用.

  2. 引數的順序可以隨意.

@class

  1. 當兩個類相互包含的時候. 當Person.h中包含Book.h 而Book.h中又包含Person.h
    這個時候,就會出現迴圈引用的問題. 就會造成無限遞迴的問題,而導致無法編譯通過.

  2. 解決方案:
    其中一邊不要使用#import引入對方的標頭檔案.
    而是使用@class 類名; 來標註這是1個類.這樣子就可以在不引入對方標頭檔案的情況下,告訴編譯器這是1個類.

    在.m檔案中再#import對方的標頭檔案.就可以使用了.

  3. @class與#import的區別

    1). #import是將指定的檔案的內容拷貝到寫指令的地方.

    2). @class 並不會拷貝任何內容. 只是告訴編譯器,這是1個類,這樣編譯器在編譯的時候才可以知道這是1個類.

迴圈retain

  1. 當兩個物件相互引用的時候.
    A物件的屬性是B物件 B物件的屬性是A物件.
    這個時候 如果兩邊都使用retain 那麼就會發生記憶體洩露.

  2. 解決方案: 1端使用retain 另外1端使用assign 使用assign的那1端在dealloc中不再需要release了.

自動釋放池(瞭解)

自動釋放池的原理.

存入到自動釋放池中的物件,在自動釋放池被銷燬的時候.會自動呼叫儲存在該自動釋放池中的所有物件的release方法.

可以解決的問題:
將建立的物件,存入到自動釋放池之中. 就不再需要手動的relase這個物件了.
因為池子銷燬的時候 就會自動的呼叫池中所有的物件的relase。

自動釋放池的好處: 將建立的物件儲存到自動釋放池中,不需要再寫release

如何建立自動釋放池.
@autoreleasepool

{

}
這對大括弧代表這個自動釋放池的範圍.
如何將物件儲存到自動釋放池之中

在自動釋放池之中呼叫物件的autorelease方法.就會將這個物件存入到當前自動釋放池之中.

這個autorealse方法返回的是物件本身. 所以,我們可以這麼寫

@autoreleasepool

{

Person *p1 = [[[Person alloc] init] autorelease];

}

這個時候,當這個自動釋放池執行完畢之後,就會立即為這個自動釋放池中的物件傳送1條release訊息.

目前為止,我們感受到得autorelase的好處:
建立物件,呼叫物件的autorelase方法 將這個物件存入到當前的自動釋放池之中.

我們就不需要再去relase 因為自動釋放池銷燬的時候 就會自動的呼叫池中所有物件的relase

使用注意

1). 只有在自動釋放池中呼叫了物件的autorelease方法,這個物件才會被儲存到這個自動釋放池之中.

如果只是將物件的建立程式碼寫在自動釋放之中,而沒有呼叫物件的autorelease方法.是不會將這個物件儲存到這個自動釋放池之中的.

2). 物件的建立可以在自動釋放池的外面,在自動釋放池之中,呼叫物件的autorelease方法,就可以將這個物件儲存到這個自動釋放池之中.

3). 當自動釋放池結束的時候.僅僅是對儲存在自動釋放池中的物件傳送1條release訊息 而不是銷燬物件.

4). 如果在自動釋放池中,呼叫同1個物件的autorelease方法多次.就會將物件儲存多次到自動釋放池之中.

在自動釋放池結束的時候.會為物件傳送多條release訊息.那麼這個是就會出現殭屍物件錯誤.
所以,1個自動釋放池之中,只autorelease1次,只將這個物件放1次, 否則就會出現野指標錯誤.

5). 如果在自動釋放池中,呼叫了儲存到自動釋放中的物件的release方法.

在自動釋放池結束的時候,還會再呼叫物件的release方法.
這個時候就有有可能會造成野指標操作.

也可以呼叫儲存在自動釋放池中的物件的retain方法.

6). 將物件儲存到自動釋放池,並不會使物件的引用計數器+1

所以其好處就是:建立物件將物件儲存在自動釋放池,就不需要在寫個release了.

7). 自動釋放池可以巢狀.

呼叫物件的autorelease方法,會講物件加入到當前自動釋放池之中
只有在當前自動釋放池結束的時候才會像物件傳送release訊息.

autorelease的規範.

0). 建立物件,將物件儲存到自動釋放池之中. 就不需要再去手動的realse。

1). 類方法的第1個規範:

一般情況下,要求提供與自定義構造方法相同功能的類方法.這樣可以快速的建立1個物件.

2). 我們一般情況下,寫1個類. 會為我們的類寫1個同名的類方法,用來讓外界呼叫類方法來快速的得到1個物件.

規範:使用類方法建立的物件,要求這個物件在方法中就已經被autorelease過了.

這樣,我們只要在自動釋放池中, 呼叫類方法來建立物件, 那麼建立的物件就會被自動的加入到自動釋放中.

提供1個類方法來快速的得到1個物件.
規範
a. 這個類方法以類名開頭. 如果沒有引數就直接是類名 如果有引數就是 類名WithXX:
b. 使用類方法得到的物件,要求這個物件就已經被autorelease過了.

-(instancetype)person{ return [[[self alloc] init] autorelease]; }

這樣,我們直接呼叫類方法.就可以得到1個已經被autorelease過的物件.

@autoreleasepool
{

Person *p1 = [Person person];
//這個p1物件已經被autorelase過了.不需要再呼叫autorelase
//這個p1物件就被儲存到當前自動釋放池之中.
}//當自動釋放池結束.就會為儲存在其中的p1物件傳送release訊息.

實際上Apple的框架中的類也是遵守這個規範的.

通過類方法建立的物件都是已經被autorelease過的了.

所以,我們也要遵守這個規範. 類方法返回的物件也要被autorealse過.

以後,我們凡事建立物件是呼叫類方法建立的物件 這個物件已經是被autorelease過的了.

arc機制概述

什麼是ARC

Automatic Reference Counting,自動引用計數. 即ARC.
顧名思義:系統自動的幫助我們去計算物件的引用計數器的值,

可以說是WWDC2011和iOS5引入的最大的變革和最激動人心的變化.
ARC是新的LLVM3.0編譯器的一項特性,使用ARC,可以說一舉解決了廣大iOS開著所憎恨的手動管理記憶體的麻煩.

在程式中使用ARC非常簡單,只需要像往常那樣編寫程式碼.
只不過永遠不要寫retain、release、autorelease 永遠要手動的呼叫dealloc 這三個關鍵字就好,這是ARC的最基本的原則.
當ARC開啟時, 編譯器會自動的在合適的地方插入retain、release、autorelase程式碼.
編譯器自動為物件做引用計數. 而作為開發者,完全不需要擔心編譯器會做錯(除非開發者自己錯用了ARC).

需要特別注意的是: ARC是編譯器機制. 在編譯器編譯程式碼的時候,會在適時的位置加入retain、release和autorealse程式碼.

ARC機制下,物件何時被釋放

本質: 物件的引用計數器為0的時候,自動釋放.

表象: 只要沒有強指標指向這個物件,這個物件就會立即回收.

強指標與弱指標.

強指標: 預設情況下,我們宣告1個指標 這個指標就是1個強指標.
我們也可以使用__strong來顯示的宣告這是1個強指標.

Person *p1; 這是1個強指標. 指標預設情況下都是1個強指標.
__strong Person *p2; 這也是1個強指標.使用__strong來顯示的宣告強指標.

弱指標: 使用__weak標識的指標就叫做弱指標.

無論是強指標還是弱指標,都是指標,都可以用來儲存地址,這1點沒有任何區別 。
都可以通過這個指標訪問物件的成員.
唯一的區別就是在ARC模式下.他們用來作為回收物件的基準.

如果1個物件沒有任何強型別的指標指向這個物件的時候,物件就會被立即自動釋放

確認程式是否開啟ARC機制.

1).預設情況下,Xcode開啟ARC機制.
2).ARC機制下,不允許呼叫retain、relase、retainCount、autorelease方法.
3).在dealloc中 不允許[super dealloc];

演示第1個ARC案例
int main(int argc, const char * argv[])

{

@autoreleasepool

{

Person *p1 = [Person new];//p1是1個強指標.

//因為我們說過,每1個指標變數預設情況下都是1個強指標變數.

NSLog(@"------");

}//當執行到這裡的時候.p1指標被回收,那麼Person物件就沒有任何

//強指標指向它了. 物件就在這被回收.

return 0;

}

第一個arc程式

ARC下的單個物件的記憶體管理.

在ARC的機制下: 當1個物件沒有任何的強指標指向它的時候 這個物件就會被立即回收.

1). 當指向物件的所有的強指標被回收的時候,物件就會被立即回收.

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

{

@autoreleasepool

{

Person *p1 = [Person new];//p1是1個強指標.

Person *p2 = p1;//p2也是個強指標.p1和p2都指向Person物件.

//因為我們說過,每1個指標變數預設情況下都是1個強指標變數.

NSLog(@"------");

}//當執行到這裡的時候.p1指標被回收,p2指標也被回收.那麼Person物件就沒有任何

//強指標指向它了. 物件就在這被回收.

return 0;

}

2).將所有指向物件的強指標賦值為nil的時候.物件就會被立即回收.

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

{

@autoreleasepool

{

Person *p1 = [Person new];//p1是1個強指標.

//因為我們說過,每1個指標變數預設情況下都是1個強指標變數.

p1 = nil;//當執行到這句話的時候.p1賦值為nil.

//p1指標不再執行Person物件.

//Person物件沒有被任何的指標所指向,所以.Person物件在這裡被釋放.

NSLog(@"------");

}

return 0;

}

這兩種情況就叫做沒有任何強指標指向物件.
1). 指向物件的所有強指標被回收掉
2). 指向物件的所有的強指標賦值為nil

強指標與弱指標.

1). 強指標與弱指標的宣告.

預設情況下,所有的指標都是強型別的,也就是說我們之前宣告的指標變數都是強類型別的

p1指標是強型別的,因為預設情況下指標都是強型別的.
Person *p1 = [[Person alloc] init];

不過我們可以使用__strong來顯示的標識指標是強型別指標.
__strong Person *p2 = [Person new];
這個時候p2指標型別是強指標型別的.其實寫不寫__strong都是強型別的指標.

指標型別也可以是弱指標型別.
使用__weak標識指標的型別是弱型別指標.
__weak Person *p3 = p2;
這個時候,p3指標就是1個弱型別的指標. p3弱指標也指向p2指標指向的物件.

在操作物件的時候,通過強指標或者弱指標都可以操作,沒有任何區別.

2). ARC模式下的物件回收標準

ARC機制下釋放1個物件的標準是: 沒有任何強指標指向物件的時候,物件就會被釋放.
如果這個時候有弱指標指向,也會被釋放.

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

{

@autoreleasepool

{

//使用strong來標識p1指標是強型別的,其實不寫strong也是強型別的.

__strong Person *p1 = [[Person alloc] init];

//使用__weak標識指標p2的型別是弱型別指標.

__weak Person *p2 = p1;

//這個時候,p2指標和p1指標都指向Person物件.

//這個時候如果設定p1的值為nil

p1 = nil;

//這個時候Person物件只有被1個弱指標p2指向,沒有任何強指標指向

//所以Person物件在這裡被回收.

}

return 0;

}

3).最重要的1點:不能建立物件用1個弱指標儲存這個物件的指標.
這樣的話,剛建立出來的物件,就沒有任何強指標指向,建立出來就會被回收.

int main(int argc, const char * argv[])
{
@autoreleasepool
{
//建立1個物件,將這個物件的地址賦值給1個弱指標
//後果就是建立出來的這個物件沒有被任何強指標指向.
//剛建立出來就會被釋放.
__weak Person *p1 = [[Person alloc] init];

}
return 0;
}

4). 在ARC機制下. 當物件被回收的時候. 原來指向這個物件的弱指標會被自動設定為nil

arc機制下的多個物件的記憶體管理

  • ARC機制下的物件的回收的標準: 當沒有任何強型別的指標指向物件的時候,這個物件就會被立即回收.

  • 強型別指標 弱型別指標.

  • 什麼情況下叫做物件沒有強指標向指向.

    1). 指向物件的強指標被回收.

    2). 指向物件的強指標被賦值為nil

  • 在ARC的機制下,@property引數不能使用retain

    因為retain代表生成的setter方法是MRC的標準的記憶體管理程式碼.
    而我們在ARC的機制下 不需要這些程式碼.

    所以,在ARC機制下的setter方法 什麼都不需要做.直接賦值就可以了.

  • ARC機制下,我們關注的重點.

    當1個類的屬性是1個OC物件的時候.這個屬性應該宣告為強型別的還是弱型別的.
    很明顯,應該宣告為1個強型別的.

    問題來了?

    如何控制@property生成的私有屬性,是1個強型別的還是1個弱型別的呢?

    使用引數, strong和weak

    @property(nonatomic,strong)Car *car;
    代表生成的私有屬性_car 是1個強型別的.

    @property(nonatomic,weak)Car *car;
    代表生成的私有屬性_car 是1個弱型別的.

    如果不寫,預設是strong.

  • 使用建議.

    1). 在ARC機制下.如果屬性的型別是OC物件型別的.絕大多數場景下使用strong
    2). 在ARC機制下.如果屬性的型別不是OC物件型別的.使用assign

    3). strong和weak都是應用在屬性的型別是OC物件的時候. 屬性的型別不是OC物件的時候就使用assign.

    在ARC機制下,將MRC下的retain換位strong

    @property(nonatomic,strong)Car *car;
    做的事情:
    1). 生成私有屬性.並且這個私有屬性是strong
    2). 生成getter setter方法的宣告
    3). 生成getter setter方法的宣告

    setter的實現:直接賦值.

ARC 迴圈引用

在ARC機制下.當兩個物件相互引用的時候.如果兩邊都使用strong 那麼就會先記憶體洩露.

解決方案: 1端使用strong 1端使用weak

@property 引數總結

  1. 開發程式分為ARC和MRC

  2. 與多執行緒相關的引數.
    atomic : 預設值 安全,但是效率低下.
    nonatomic: 不安全,但是效率高.

    無論在ARC還是在MRC都可以使用.
    使用建議: 無論是ARC還是MRC 都使用nonatomic

  3. retain:

    只能用在MRC的模式下.代表生成的setter方法是標準的記憶體管理程式碼.
    當屬性的型別是OC物件的時候.絕大多數情況下使用retain. 只有在出現了迴圈引用的時候1邊retain 1邊assign

  4. assign:
    在ARC和MRC的模式下都可以使用assign.
    當屬性的型別是非OC物件的時候 使用assign.

  5. strong:
    只能使用在ARC機制下. 當屬性的型別是OC物件型別的時候,絕大多數情況下使用strong]
    只有出現了迴圈引用的時候, 1端strong 1端weak

  6. weak:

    只能使用在ARC機制下. 當屬性的型別是OC物件的時候. 只有出現了迴圈引用的時候, 1端strong 1端weak

  7. readonly readwrite

    無論是ARC還是MRC 都可以使用.

  8. setter getter 無論在ARC下還是在MRC下都可以改.

在ARC機制下.原來使用retain的用strong
出現迴圈引用的時候. MRC: 1邊retain 1邊assign ARC: 1邊strong 1邊weak

MRC與ARC的相容

  1. 有可能會遇到的問題.

    ​程式使用的是ARC機制開發的,但是其中的某些類使用的是MRC.

  2. 使用命令. ` -fno-objc-arc

補充

  1. ARC機制和垃圾回收機制的區別.

GC: 程式在執行的期間,有1個東西叫做垃圾回收器.不斷的掃描堆中的物件是否無人使用.

Person *p1 = [Person new];
p1 = nil;

ARC: 不是執行時. 在編譯的時候就在合適的地方插入retain......
插入的程式碼足以讓物件無人使用的時候 引用計數器為0

相關文章