內(nèi)存分配
- 棧 stack
- 由系統(tǒng)管理溶褪,分配和釋放
- 存儲局部變量,保存函數(shù)現(xiàn)場
- 連續(xù)的內(nèi)存地址辨图,由高向低分配班套,不會產(chǎn)生碎片
- 效率高。棧是機器系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu)徒役,計算機會在底層對棧提供支持:分配專門的寄存器存放棧的地址孽尽,壓棧出棧都有專門的指令執(zhí)行窖壕,這就決定了棧的效率比較高忧勿。
- 類似于數(shù)據(jù)結(jié)構(gòu)中的棧杉女,先進后出
每一個方法執(zhí)行的時候都會向棧區(qū)申請內(nèi)存,這部分內(nèi)存隨著方法的結(jié)束而釋放鸳吸,由系統(tǒng)自動分配熏挎。棧區(qū)的大小是事先規(guī)定好的(2M / 1M),如果申請的空間超過剩余可用空間就會發(fā)生 stackoverflow晌砾。
- 堆 heap
- 開發(fā)者手動管理 alloc release
- 不連續(xù)的內(nèi)存地址坎拐,由低向高分配,容易產(chǎn)生碎片
- 分配方式類似鏈表养匈,先進先出
- 效率不如棧哼勇。計算機底層并沒有對堆的支持,堆是有 C/C++ 函數(shù)庫提供的呕乎,加上碎片問題积担,導致堆的效率比棧低。
系統(tǒng)會有個記錄空閑地址的鏈表猬仁,當系統(tǒng)收到內(nèi)存申請的時候帝璧,就去遍歷該鏈表,尋找第一個空間大于申請空間的堆結(jié)點湿刽,然后將該結(jié)點刪除的烁,并將該結(jié)點的空間分配給程序。如果分配的空間有多余诈闺,系統(tǒng)將會把多余的空間重新放回鏈表中渴庆。雖然程序結(jié)束后,所以的數(shù)據(jù)空間都會被系統(tǒng)回收雅镊,但是精確的申請與釋放內(nèi)存是我們必備的素質(zhì)把曼。
- 全局區(qū) / 靜態(tài)區(qū)
- 存儲全局變量、靜態(tài)變量
- bbs:未初始化漓穿;data:初始化的
- 常量區(qū)
- 常量字符串
- 代碼區(qū)
- 存儲 App 二進制代碼
ARC 與 MRC
對象操作
- alloc/new/copy/mutableCopy 生成并持有對象嗤军,retainCount: +1
- retain 持有對象,retainCount: +1
- release 釋放對象晃危,retainCount: -1
- dealloc 廢棄對象
內(nèi)存管理基本規(guī)則
- 生成并持有對象
- 持有非自己生成的對象
- 不再需要自己持有對象的時候釋放
- 非自己持有的對象無法釋放
ARC 原理
ARC 是蘋果在 LLVM 3.0 開始引用一種內(nèi)存管理機制叙赚,會根據(jù)對象的引用計數(shù)自動監(jiān)控對象的生命周期,實現(xiàn)方式是在編譯時期自動在已有的代碼中插入合適的內(nèi)存管理代碼以及在 Runtime 做一些優(yōu)化僚饭。
所有權(quán)修飾符
ARC 規(guī)定每個對象前必須加上所有權(quán)修飾符:
- __strong
默認的所有權(quán)修飾符震叮,表示對對象的強引用,持有強引用的對象在超過其作用域時被廢棄
// ARC 無效
{
id obj = [[NSObject alloc] init];
[obj release];
}
// ARC 有效
{
id __strong obj = [[NSObject alloc] init];
}
- __weak
表示對對象的弱引用鳍鸵,主要用于避免循環(huán)引用苇瓣,如果對象沒有強引用了,弱引用會被置為 nil - __unsafe_unretained
不安全所有權(quán)修飾符偿乖,同 __weak 作用一樣击罪,只是在對象沒有強引用的時候不會被置為 nil抽诉,該指針就變成了懸垂指針 - __autorelesing
用于替代 autorelease 方法
屬性修飾符
賦值性
- strong 強引用虐块,先保留新值骏全,再釋放舊值网持,最后賦值
- (void)setValue:(id)newValue {
[newValue retain];
[_value release];
_value = newValue;
}
- retain 在 ARC 下與 strong 類似,MRC 下修飾 block 時竣稽,strong 會把 block 拷貝到堆區(qū)囱怕,retain 不會
- weak 弱引用,既不保留新值也不釋放舊值毫别,當對象被釋放時娃弓,自動指向 nil
- unsafe_unretained 與 weak 一樣,只是當對象被釋放時岛宦,不會自動指向 nil
- assign 賦值操作忘闻,通常用于基本數(shù)值類型,不涉及應用計數(shù)恋博,默認屬性
-
copy 類似于 strong齐佳,不過在賦值的時候進行的是
copy
而不是retain
操作,一般用于修飾不可變對象( NSString )和 block
讀寫性
-
readonly 只讀特性债沮,只生成
getter
方法 -
readwrite 可讀可寫特性炼吴,生成
setter
、getter
方法疫衩,默認屬性
原子性
-
nonatomic 非原子特性硅蹦,不加同步,多線程訪問時效率高闷煤,但是線程不安全童芹,
setter
、getter
不是原子操作 -
atomic 原子特性鲤拿,同步操作假褪,多線程安全(不是絕對,因為)近顷,
setter
生音、getter
是原子操作,默認屬性
指定方法名
- getter = <getMethodName>窒升,setter = <setMethodName>
相關問題
NSString 為什么要用 copy 修飾缀遍?
NSMutableString 是 NSString 的子類,所以可以把一個 NSMutableString 賦值給 NSString饱须,這樣就不能保證 NSString 不可變性了域醇,所以最好使用 copy 修飾,這樣賦值的時候就會先拷貝新值然后賦值,不管可變不可變都會變成不可變字符串譬挚。
block 為什么要用 copy 修飾锅铅?
在 MRC 環(huán)境下時,block 默認是放在棧里面的殴瘦,所以要用 copy 修飾狠角,將其拷貝到堆區(qū)号杠,避免釋放蚪腋。而在 ARC 環(huán)境下,用 storng 修飾姨蟋,系統(tǒng)也會自動將其拷貝到堆區(qū)屉凯,所以用 strong 和 copy 修飾都是可行的,但是蘋果建議我們用 copy 修飾眼溶,這樣能顯式地指明內(nèi)存行為悠砚,官方文檔描述如下:
Note: You should specify copy as the property attribute, because a block needs to be copied to keep track of its captured state outside of the original scope. This isn’t something you need to worry about when using Automatic Reference Counting, as it will happen automatically, but it’s best practice for the property attribute to show the resultant behavior.
atomic 為什么不是一定線程安全的?
atomic 只是表明該屬性的 setter
堂飞、getter
是線程安全的。
ARC 優(yōu)點
- 減少工作量绰筛,無需鍵入 retain 或者 release 代碼
- 降低程序崩潰、內(nèi)存泄漏等風險
- 編譯器完全清楚目標對象铝噩,并能立即釋放那些不再被使用的對象衡蚂,這樣應用程序就具有可預測性骏庸,且能流暢運行,速度也將大幅提升
不要使用 retainCount
retainCount 在實際的 release 環(huán)境中是沒有什么作用的具被,在 debug 的時候,retainCount 返回的數(shù)值也不一定準確一姿,因為系統(tǒng)會對 retainCount 進行優(yōu)化补箍,比如當一個對象的 retainCount 為 1 的時候啸蜜,再對它進行 release 操作,系統(tǒng)可能只是標記該內(nèi)存可回收衬横,沒必要再繼續(xù)一次 -1 的數(shù)值操作,這個時候返回的 retainCount 就不會為 0。而且對于加入自動釋放池的對象遥诉,對其發(fā)送 retainCount 消息是一件很危險的事情,因為無法確定其是否被釋放矮锈,或者內(nèi)存是否被復用,如果被其他對象復用苞笨,返回的 retainCount 必然是有問題的债朵。
循環(huán)引用
- 主動釋放
- 弱引用
- weak 可以為空
- unowned 不能為空