說到OC的內(nèi)存管理, 就不得不說一說OC的內(nèi)存管理機制, OC是通過引用計數(shù), 來決定對象是否釋放, 引用計數(shù)為0, 對象對應的內(nèi)存便被釋放, 否則, 該片內(nèi)存就不會被釋放瞎惫。
本文主要大概解釋一下內(nèi)存管理的底層運行機制, 對具體源碼實現(xiàn)不做分析, 主要通過定性結論以及一些自己的小demo起到一個科普的作用, 如想深入研究, 網(wǎng)上解析源碼的博客不勝枚舉, 希望本文能在您深入研究前對您有些許幫助灼芭。
MRC時代, 程序員需要自己管理對象的引用計數(shù), 通過retain, release來控制對象的引用計數(shù), 代碼冗余不說, 釋放非自己持有的對象還會造成crash寿弱。筆者并未經(jīng)歷過MRC時代, 沒有深刻的體會, 再此不過多贅述, 下面我們聊一聊ARC下的內(nèi)存管理削葱。
隨著ARC的出現(xiàn), 隨之也出現(xiàn)了__strong, __weak指針, 那么它們又各有什么樣的作用呢?
作為一名iOS開發(fā)者, 不可能對這個問題不知道, 簡單來說, strong指針就是對一個對象持有, 使之引用計數(shù)+1, 而weak指針僅僅指向該對象, 并不持有該對象, 當該對象被釋放時, weak指針自動置為nil男图”囊桑可以看出, weak指針很安全, 至少不會非法訪問而造成crash商源。
那么, 新的問題來了, strong 和 weak關鍵字的優(yōu)化各是發(fā)生在什么時期呢?
- strong指針: 是在編譯階段進行處理, 實際就是編譯器給加上 retain release
- weak指針: 是在運行期處理, weak指針賦值的時候, 不會對該對象進行retain, 在該對象引用計數(shù)為0時, 置為 nil, 事實上是在該對象引用計數(shù)即將變?yōu)?之前將該指針置為nil(此步驟便是運行期進行處理的)
- weak實現(xiàn)原理: 運行時底層會維護一個離散表, 我們可以理解為類似hashMap或NSDictionary結構的東西, 存放所有weak對象的相關信息, {對象地址 : weak指針的指針(二級指針)}, 當對象即將dealloc時, 來檢查這個表, 從而改變weak指針的指向, 置為nil
這里我們可以聯(lián)想到當我們要傳一個NSError *error時, 傳的是&error, 也是一個二級指針, 只有這樣, 才能修改error指針的指向, 如果傳的是一級指針, 只能修改指針指向的那塊內(nèi)存的內(nèi)容。
結論既然已經(jīng)拋出, 我們用幾個小demo來驗證一下并加深印象, 首先先說一下大體結構: 有 FirstViewController 和 SecondViewController 兩個控制器 SecondViewController 是通過 FirstViewController modal出來的
FirstViewController 中代碼
extern id strongVc;
extern __weak id weakVc;
id strongVc = nil;
__weak id weakVc = nil;
- (void)touchesBegan:(NSSet<UITouch> *)touches withEvent:(UIEvent *)event {
NSLog(@"%s, ------------%@", __func__, strongVc);
// 當從SecondViewController dismiss回來后, 點擊屏幕 BAD_ACCESS 非法訪問,
// 說明調(diào)用 dealloc, 不管有沒有強引用, 對象對應的那塊內(nèi)存都會被釋放
}
SecondViewController 中代碼
- (void)viewDidLoad {
[super viewDidLoad];
weakVc = self; // 給weakVc賦值
}
- (void)dealloc {
NSLog(@"weakVc = %@", weakVc);
// weakVc = (null), 證明了 調(diào)用dealloc完成前, weak指針已經(jīng)被置為nil
NSLog(@"self = %@", self);
// self = <SecondViewController: 0x7ffe37f0de40>**
strongVc = self;
// 此時使一個強指針指向 self
}
以上注釋, 就是操作結果驻子。
此外, 我們知道ARC 不允許主動掉dealloc方法, 那么如果我們強行調(diào)用dealloc方法會怎樣呢?
在SecondViewController添加以下代碼
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self performSelector:NSSelectorFromString(@"dealloc")];
NSLog(@"%s, ------------%@", __func__, weakVc); // nil
NSLog(@"%s, ------------%@", __func__, self); // BAD_ACCESS -> crash
}
強行調(diào)用dealloc, 當訪問到self時, BAD_ACCESS 導致奔潰, 進一步證明, 只要調(diào)用dealloc方法, 會把該對象對應的那部分內(nèi)存釋放灿意。
以上, 是我一些小小的分享, 希望能讓大家大體上有個概念, 如果有興趣進一步了解, 可以研究研究《Objective-C高級編程》, 里面很細致的講解了與內(nèi)存管理的相關實現(xiàn),可以有助于我們更好地了解OC的內(nèi)存管理機制。