iOS記憶體管理
記憶體管理原則
- 自己生成的物件,自己持有
- 非自己生成的物件,自己也能持有
- 不在需要自己持有的物件時釋放
- 非自己持有的物件無法釋放
自己生成的物件,自己持有
使用 alloc/new/copy/mutableCopy 名稱開頭的方法名意味著自己生成的物件只能自己持有!
// 自己生成並持有物件
id obj = [[NSObject alloc] init];
id obj1 = [NSObject new];
使用NSObject類的alloc類方法就能自己生成並持有物件。指向生成並持有物件的指標被賦給變數obj。另外,使用new類方法也能生成並持有物件,兩中方式是完全一致的。
而copy方法則利用基於NSCopying方法約定,由各類實現的copyWithZone:
非自己生成的物件,自己也能持有
// 取得非自己生成的物件,但並不持有
id obj = [NSMutableArray array];
// 自己持有物件
[obj retain];
原始碼中,NSMutableArray類物件被賦值給變數obj,但變數obj自己並不持有該物件,所以使用retain方法可以持有物件。
不再需要自己持有的物件時釋放
自己持有的物件,一旦不再需要,持有者有義務釋放該物件。
// 自己生成並持有物件
id obj = [[NSObject alloc] init];
// 釋放物件
[obj release];
如此,自己生成並持有的物件就通過release方法釋放了。需要注意的是,物件一旦釋放絕對不可訪問。
那麼,呼叫[NSMutableArray array]
方法取得的物件存在,但自己不持有物件是如何實現的呢?
- (id)object
{
// 自己持有物件
id obj = [[NSObject alloc] init];
// 自動釋放
[obj autorelease];
return obj;
}
上例中,使用了autorelease方法。用該方法,可以使取得的物件存在,但自己卻不持有物件。autorelease提供這樣的功能,使物件在超出指定的生存範圍時能夠自動並正確地釋放(呼叫release方法)。
呼叫release方法物件會立即釋放。但呼叫autorelease方法不會立即釋放,而是把物件註冊到autoreleasepool中,經過一段時間後,pool結束時讓裡面的物件自動呼叫release方法,這個後面會詳細說明!
無法釋放非自己持有的物件
對於用alloc/new/copy/mutableCopy方法生成並持有的物件,或是用retain方法持有的物件,由於持有者是自己,所以在不需要該物件的時候將其釋放。而由此以外所得到的物件絕對不能釋放,因為釋放了非自己所持有的物件就會造成崩潰!
example1:自己生成並持有的物件,在釋放完後再次釋放
// 自己生成並持有物件
id obj = [[NSObject alloc] init];
// 釋放物件
[obj release];
// 釋放之後再次釋放非自己持有的物件
// 崩潰資訊:訪問一塊壞記憶體
[obj release];
example2:在“取得的物件存在,但自己不持有物件”時釋放
// 取得非自己生成的物件,但並不持有
id obj = [NSMutableArray array];
[obj release];
記憶體管理的引用計數實現原理
蘋果對引用計數的實現是:採用引用計數表管理引用計數(下面是列舉的兩個好處)
- 物件用記憶體塊的分配無需考慮記憶體塊頭部
- 引用計數表各記錄中存有記憶體塊地址,可從各個記錄追溯都各個物件的記憶體塊
所以可知,第二條在除錯的時候有很重要的作用。即使出現故障導致物件佔用的記憶體塊損壞,但只要引用計數表沒有被損壞,就能夠確認各記憶體塊的位置。而且在利用工具檢測記憶體洩漏時,引用計數表的各記錄也有助於檢測各物件的持有者是否存在。
Autorelease
Autorelease的使用
autorelease從名字上來看,意思就是“自動釋放”,它類似於C語言中區域性變數的特性。當超出其作用域時,物件例項的release例項方法會被呼叫。
autorelease使用如下:
- 生成並持有NSAutoreleasePool物件
- 呼叫已分配物件的autorelease例項方法
- 廢棄NSAutoreleasePool物件
而且對於所有呼叫過autorelease例項方法的物件,在廢棄NSAutoreleasePool物件時,都將呼叫release例項方法。
Autorelease的實現
在GNUstep的原始碼中,autorelease的實現為如下程式碼:
- (id)autorelease
{
[NSAutoreleasePool addObject:self];
}
可以看出,autorelease例項方法的本質就是呼叫NSAutoreleasePool物件的addObject類方法。
當呼叫NSObject類的autorelease例項方法時,該物件會被追加到正在使用的NSAutoreleasePool物件中的數組裡.
所有權修飾符
Objective-C中為了處理物件,可將變數型別定義為id型別或各種物件型別。
- 物件型別:指向NSObject這樣的Objective-C類的指標
- id型別:用於隱藏物件型別的類名部分,相當於C語言中常用的void *
ARC有效時,id型別和物件型別同C語言其他型別不同,其型別上必須附加所有權修飾符,一共有四種:
- __strong
- __weak
- __unsafe _unretained
- __autoreleasing
__strong 修飾符
__strong修飾符是id型別和物件型別預設的所有權修飾符。
// 下面兩句程式碼效果是一樣的
id obj = [[NSObject alloc] init];
id __strong obj = [[NSObject alloc] init];
__strong修飾對物件的“強引用”。持有強引用的變數在超出其作用域時被廢棄,隨著強引用的實效,引用的物件會隨之釋放。
{
// 自己生成並持有物件
// 因為變數obj為強引用,所以自己持有物件
id __strong obj = [[NSObject alloc] init];
}
/*
* 因為變數obj超出其作用域,強引用實效。
* 所以自動釋放自己持有的物件,物件的所有者不存在,因此廢棄該物件
*/
__strong修飾符的變數,不僅只在變數作用域中,在賦值上也能正確地管理其物件的所有者,而且Objective-C類成員變數,也可以在方法引數上,使用附有__strong修飾符的變數。
所以很明顯,__strong修飾符很符合“引用計數式記憶體管理的思考方式”。“自己生成的物件,自己持有”和“非自己生成的物件自己也能持有”只需通過對帶__strong修飾符的變數賦值便可達成。通過廢棄帶__strong修飾符的變數或者對變數賦值,就可以做到“不再需要自己持有的物件時釋放”。而由於不再鍵入release,所以“非自己持有的物件無法釋放”根本就不可能執行。
__weak 修飾
__strong是有一個致命的缺點,那就是容易出現兩個物件相互引用(迴圈引用容易發現記憶體洩漏)和一個物件自引用,而__weak就可以避免這種現象。
{
// 自己生成並持有物件
id __strong obj0 = [[NSObject alloc] init];
// obj1變數持有生成物件的弱引用
id __weak obj1 = obj0;
}
/**
* 因為obj0變數超出其作用域,強引用實效,所以自動釋放自己的持有的物件
* 而物件的所有者不存在,所以廢棄該物件
*/
帶__weak修飾符的變數不持有物件,所以在超出其變數作用域的時候,物件即被釋放。
__weak修飾符還有另一個優點。在持有某物件的弱引用時,若該物件被廢棄,則此弱引用將自動失效,並且處於nil被賦值的狀態。
id __weak obj1 = nil;
{
id __strong obj0 = [[NSObject alloc] init];
obj1 = obj0;
// 輸出obj1變數持有的弱引用的物件
NSLog(@"A: %@", obj1);
}
/**
* 因為obj0變數超出其作用域,強引用失效,所以自動釋放自己持有的物件
* 物件無持有者,所以廢棄該物件
*
* 廢棄該物件的同時,持有該物件弱引用的obj1變數的弱引用失效,nil賦值給obj1
*/
NSLog(@"B: %@", obj1);
列印結果:
因此,使用__weak可以避免迴圈引用,而且通過檢查附有__weak修飾符的變數是否為nil,可以判斷被賦值的物件是否已廢棄。
__unsafe _unretained 修飾符
ARC式的記憶體管理是編譯器的工作,但附有__unsafe _unretained修飾符的變數不屬於編譯器的記憶體管理物件。
__unsafe _unretained修飾符和__weak修飾符其實類似,在上面的程式碼中把修飾符改為__unsafe _unretained後,列印結果就為:
需要注意的是:賦值給附有__unsafe _unretained修飾符變數的物件在通過該變數使用時,如果沒有確保其確實存在,那麼應用程式就會崩潰
__autoreleasing 修飾符
ARC有效時,不能使用autorelease方法,也不能使用NSAutoreleasePool類。不過實際上,ARC有效時autorelease功能也是起作用的。
ARC有效時,@autoreleasePool塊代替了NSAutoreleasePool類,__autoreleasing修飾符代替了autorelease方法。 而且__autoreleasing修飾符和__strong一樣,一般都是非顯式的附加。
注意點:編譯器會自動檢查方法名是否以alloc/new/copy/mutableCopy開始,如果不是則自動將返回值的物件註冊到autoreleasePool中,而且根據命名規則,init方法返回值的物件不註冊到autoreleasePool中
在ARC有效時,[NSMutableArray array]
方法的內部實現如下:
+ (id)array
{
id obj = [[NSMutableArray alloc] init];
return obj;
}
由於return使得物件變數超出其作用域,所以該強引用對應的自己持有的物件會被自動釋放,但該物件作為函式返回值,編譯器會自動將其註冊到autoreleasePool。
而且在訪問附有__weak修飾符的變數時,實際上必定要訪問註冊到autoreleasePool的物件。因為__weak修飾符持有物件的弱引用,而在訪問引用物件的過程中,該物件有可能會被廢棄。所以為了確保該物件存在,就會把要訪問的物件註冊到autoreleasePool中,那麼在@autoreleasePool塊結束之前都能讓其存在。
ARC規則
在ARC有效的情況下,必須遵守一定的規則。規則如下:
- 不能使用retain/release/retainCount/autorelease
- 不能使用NSAllocateObject/NSDeallocateObject
- 須遵守記憶體管理的方法命名規則
- 不要顯式呼叫dealloc
- 使用@autoreleasepool塊代替NSAutoreleasePool
- 不能使用區域 (NSZone)
- 物件型別變數不能作為C語言結構體的成員
- 顯式轉換id和void *
相關推薦
iOS 記憶體管理研究
iPhone 作為一個移動裝置,其計算和記憶體資源通常是非常有限的,而許多使用者對應用的效能卻很敏感,卡頓、應用回到前臺丟失狀態、甚至 OOM 閃退,這就給了 iOS 工程師一個很大的挑戰。 網上的絕大多數關於 iOS 記憶體管理的文章,大多是圍繞 ARC/MRC、迴圈引用的原理或者是如何找尋記憶體洩漏來展
Objective-C runtime機制(5)——iOS 記憶體管理
概述 當我們建立一個物件時: SWHunter *hunter = [[SWHunter alloc] init]; 上面這行程式碼在棧上建立了hunter指標,並在堆上建立了一個SWHunter物件。目前,iOS並不支援在棧上建立物件。 iOS 記憶體分割槽 iOS
iOS記憶體管理的那些事兒-原理及實現
作者簡介 boyce,餓了麼物流團隊資深iOS開發。曾在格瓦拉等公司從事iOS相關研發工作。 注:本篇文章是《iOS記憶體管理的那些事兒》系列文章的第一部分。稍後我們會持續更新第二部分(開源監測記憶體洩漏的實現)和第三部分(如何利用開源工具做相關的APM),感興趣的童鞋可以關注我們專欄並獲取實
iOS 記憶體管理(補充)
物件操作 OC中對應的方法 對應的 retain Count 變化 生成並持有物件 alloc/new/copy/mutableCopy等 +1 內容單元
iOS記憶體管理知識點梳理
1.iOS記憶體管理區域分為以下5個區域: 棧區,堆區,靜態區,常量區在記憶體分佈中以由高地址向低地址分佈的. (1).棧區(stack):它是有編譯器自動分配和管理的,存放區域性變數,函式的引數值.例如: - (NSString *)encodeBase64String:(NSString
iOS 進階—— iOS 記憶體管理 & Block
第一篇 iOS 記憶體管理 1 似乎每個人在學習 iOS 過程中都考慮過的問題 alloc retain release delloc 做了什麼? autoreleasepool 是怎樣實現的? __unsafe_unretained 是什麼? Block 是怎樣實現的 什麼
IOS記憶體管理知識總結(一)
最近優化公司在優化app,總結幾個記憶體管理的知識點。 首先我們要清楚 1. “堆”和“棧” Objective-C的物件在記憶體中是以堆的方式分配空間的,並且堆記憶體是由你釋放的,就是releaseOC物件存放於堆裡面(堆記憶體要程式設計師手動回收)非
IOS記憶體管理--自動釋放池的實現原理
原文連結:http://www.cocoachina.com/ios/20150610/12093.html 記憶體管理一直是學習 Objective-C 的重點和難點之一,儘管現在已經是 ARC 時代了,但是瞭解 Objective-C 的記憶體管理機制仍然是十分必要的。其中,弄清楚 auto
iOS記憶體管理
記憶體管理原則 自己生成的物件,自己持有 非自己生成的物件,自己也能持有 不在需要自己持有的物件時釋放 非自己持有的物件無法釋放 自己生成的物件,自己持有 使用 alloc/new/copy/mutableCopy 名稱開頭的方法名意味著自己生成的
iOS記憶體管理:記憶體洩露除錯的常用技巧
在往下看之前請下載例項MemoryProblems,我們將以這個工程展開如何檢查和解決記憶體問題。 懸掛指標問題 懸掛指標(Dangling Pointer)就是當指標指向的物件已經釋放或回收後,但沒有對指標做任何修改(一般來說,將它指向空指標),而是仍然指向原來已經回收的地址。如
iOS 進階—— iOS記憶體管理
1 似乎每個人在學習 iOS 過程中都考慮過的問題 alloc retain release delloc 做了什麼? autoreleasepool 是怎樣實現的? __unsafe_unretained 是什麼? Block 是怎樣實現的 什麼時候會引起迴圈引用,
iOS 記憶體管理基本原則
我們知道objc中建立物件是存放在堆中的(基本資料型別除外,是由系統自己管理,並存放在棧中),系統不會自動釋放堆中的記憶體。如果建立完的物件存放在堆中後並使用完沒有得到及時的釋放,會佔用的記憶體。但是
2-2 iOS 記憶體管理,棧,堆,BSS段,資料段,程式碼段,野指標,殭屍物件
記憶體管理,拆開講就是對如何將資料儲存到記憶體中,如何釋放記憶體中的資料,什麼時候釋放。記憶體中的六大區域記憶體分為5個區域,分別指的是----->棧區/堆區/BSS段/資料段/程式碼段棧:儲存區
IOS記憶體管理,ARC,MRC,自動釋放池(基礎)
在IOS中記憶體管理幾乎是每個人必須知道的一個知識點。首先我們總結一下MRC,再通過MRC來認識ARC以及自動釋放池 1.MRC 1.1 淘汰的技術 1.2 引用計數(RC)是指alloc自動分配的一塊兒儲存空間,用於儲存持有該空間的指標個數 1.3 使
Objective-C高階程式設計:iOS與OS X多執行緒和記憶體管理
這篇文章主要給大家講解一下GCD的平時不太常用的API,以及文末會貼出GCD定時器的一個小例子。 需要學習的朋友可以通過網盤免費下載pdf版 (先點選普通下載-----再選擇普通使用者就能免費下載了)http://putpan.com/fs/cy1i1beebn7s0h4u9/ 1.G
[讀書筆記]iOS與OS X多執行緒和記憶體管理 [GCD部分]
3.2 GCD的API 蘋果對GCD的說明:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。 “Dispatch Queue”是執行處理的等待佇列。通過dispatch_async函式等API,在Block
iOS面試之——記憶體管理
記憶體管理 1.什麼是ARC? ARC是automatic reference counting自動引用計數,在程式編譯時自動加入retain/release。在物件被建立時retain count+1,在物件被release時count-1,當count=0時,銷燬物件。程式中加入
理解iOS 和 macOS 的記憶體管理
在 iOS 和 macOS 應用的開發中,無論是使用 Objective-C 還是使用 swift 都是通過引用計數策略來進行記憶體管理的,但是在日常開發中80%以上的情況,我們不需要考慮記憶體問題,因為 Objective-C 2.0 引入的自動引用計數(ARC)技術為開發者們自動的完成了
iOS NSMutableArray 的記憶體管理原理
我一直想知道NSMutableArray內部如何運作。不要誤會我的意思,不可變陣列肯定會帶來巨大的好處。它們不僅是執行緒安全的,而且複製它們基本上是免費的。它並沒有改變它們非常沉悶的事實 - 它們的內容無法修改。我發現實際的記憶體操作細節令人著迷,這就是本文關注可變陣列的原因。 由於我或多或
理解 iOS 和 macOS 的記憶體管理
在 iOS 和 macOS 應用的開發中,無論是使用 Objective-C 還是使用 swift 都是通過引用計數策略來進行記憶體管理的,但是在日常開發中80%(這裡,我瞎說的,8020 原則嘛?)以上的情況,我們不需要考慮記憶體問題,因為 Objective