iOS开发——MRC(手动内存管理)
iOS開發——MRC(手動內存管理)
- 內存分配區域
- 棧區
- 堆區
- 總結
- 常量區
- 總結
- 代碼區
- 總結
- 全局區
- 關于如何查看一個對象在堆區 / 棧區
- 需要知道的知識
- 手動引用計數MRC
- 四個法則
- 非自己生成的對象,且該對象存在,但自己不持有
- dealloc
- retainCount
- 不要使用retainCount
我們要想了解內存管理的知識前,必須先搞明白計算機的內存分配以及計算機是如何處理內存的。
內存分配區域
內存指的就是RAM(random access memory),內存分配區域主要分為五個區:棧區(系統管理的地方)、堆區(程序員控制的地方)、靜態區(全局區)、常量區、代碼區。
棧區
棧區(stack)由編譯器自動分配并釋放,存放的是函數的參數值,局部變量,基本類型的變量和對象引用類型,方法調用的實參也是保存在棧區。所以我們可以把棧看做一個臨時寄存、交換的內存區。
棧是系統數據結構,對應線程/進程是唯一的。優點是快速高效,缺點是有限制,數據操作不靈活。
堆區
由程序員分配(malloc、new)和釋放(delete),主要存在new構造的對象和數組如果不釋放,可能造成內存泄露,程序結束時可能會由操作系統回收。
堆向高地址擴展的數據結構,是不連續的內存區域。 程序員負責在何時釋放內存,在ARC程序中,計數器為0的時候,在當次的runloop結束后,釋放掉內存。堆中的所有東西都是匿名的,這樣不能按名字訪問,而只能通過指針訪問。
對于堆來講,頻繁的new/delete勢必會造成內存空間的不連續性,從而造成大量的碎片 ,使程序效率降低。
總結
常量區
文字常量區存放常量字符,程序結束后自動釋放,不允許修改。
總結
代碼區
存放函數體的二進制代碼
總結
全局區
全局變量和靜態變量都被分配到同一塊內存。
全局區(靜態區) (static): 全局變量和靜態變量的存儲是放在一起的,初始化的全局變量和靜態變量存放在一塊區域,未初始化的全局變量和靜態變量在相鄰的另一塊區域,程序結束后由系統釋放。
注意:全局區又可分為未初始化全局區(BSS段)和初始化全局區(DATA段)。
關于如何查看一個對象在堆區 / 棧區
需要知道的知識
在學習MRC之前除了要了解計算機關于內存的分配區域之外,還需要了解一些知識,具體如下:
另外現在我們的XCode默認為ARC環境,如果需要MRC我們需要手動設置:
手動引用計數MRC
我們要想手動進行內存管理就需要知道哪些情況會引起計數的變化:
對象被廢棄對象所占內存解除分配 并放回“可用內存池中”。
四個法則
自己生成的對象,自己持有
/** 自己生成并持有該對象*/id obj0 = [[NSObeject alloc] init];id obj1 = [NSObeject new];非自己生成的對象,自己也能持有。
/** 持有非自己生成的對象*/ id obj = [NSArray array]; // 非自己生成的對象,且該對象存在,但自己不持有 [obj retain]; // 自己持有對象不在需要自己持有對象的時候,釋放。
/** 不在需要自己持有的對象的時候,釋放*/ id obj = [[NSObeject alloc] init]; // 此時持有對象[obj release]; // 釋放對象 /** 指向對象的指針仍就被保留在obj這個變量中* 但對象已經釋放,不可訪問*/非自己持有的對象無需釋放。
/** 非自己持有的對象無法釋放*/ id obj = [NSArray array]; // 非自己生成的對象,且該對象存在,但自己不持有[obj release]; // ~~~此時將運行時crash 或編譯器報error~~~ 非 ARC 下,調用該方法會導致編譯器報 issues。此操作的行為是未定義的,可能會導致運行時 crash 或者其它未知行為非自己生成的對象,且該對象存在,但自己不持有
這句話聽著很繞口,但其實很好理解,但是要了解這句話首先我們要知道一個叫autorelease的東西。
所謂autorelease就是自動釋放,注意:autorelease和ARC 是完全不同的兩個東西,沒有任何聯系。
類似于C語言的局部變量,超出變量作用域,局部變量就會被廢棄,不可再訪問。
autorelease會在超出作用域時,調用對象的release實例方法,與C語言不同的是,編程人員可以設定變量的作用域,類似下圖:
我們來看一下代碼:
- (id) getAObjNotRetain {id obj = [[NSObject alloc] init]; // 自己持有對象[obj autorelease]; // 取得的對象存在,但自己不持有該對象return obj; }取得的對象存在,但自己不持有該對象,那么該對象被誰持有了呢?
調用 autorelease 方法,就會把該對象放到離自己最近的自動釋放池中(棧頂的釋放池,多重自動釋放池嵌套是以棧的形式存取的),即:使對象的持有權轉移給了自動釋放池(即注冊到了自動釋放池中),調用方拿到了對象,但這個對象還不被調用方所持有。
那么問題來了,autorelease和release有什么區別?
Autorelease實際上只是把對release的調用延遲了,對于每一個Autorelease,系統只是把該對象放入了當前的Autorelease pool中,當該pool被釋放時,該pool中的所有對象會被調用Release。
autorelease pool的作用就是來避免頻繁申請/釋放內存(就是pool的作用了)。
像 [NSMutableArray array]、[NSArray array]都可以取得誰都不持有的對象,這些方法都是通過 autorelease 實現的。
autorelease 方法不會改變調用者的引用計數,它只是改變了對象釋放時機,不再讓程序員負責釋放這個對象,而是交給自動釋放池去處理。等到自動釋放池銷毀時,所有對象統一都會被釋放。
看一下下面的代碼:
int main(int argc, const char * argv[]) {NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];id obj = [[NSObject alloc]init];NSLog(@"%lu",(unsigned long)[obj retainCount]);[obj autorelease];NSLog(@"%lu",(unsigned long)[obj retainCount]);[pool drain];NSLog(@"%lu",(unsigned long)[obj retainCount]);return 0; }它的打印結果是什么呢?
首先生成對象引用計數+1,接著將持有權轉移給了自動釋放池引用計數不變,然后銷毀池子,池子中的所用對象調用release,引用計數-1,變為0。
假如我將代碼改成如下:
這樣會發生什么呢?這樣的話程序會崩潰,因為池子銷毀的時候,池子里的所有對象 都被釋放了,調用一個不存在的對象,程序當然會崩潰。
dealloc
NSObject 協議中定義的內存管理方法與遵守這些方法命名約定的自定義方法的組合提供了用于引用計數環境中的內存管理的基本模型。NSObject 類還定義了一個dealloc 方法,該方法在對象被銷毀時自動調用,也就是在引用計數為0時。
retainCount
不要使用retainCount
看一段代碼:
NSNumber *number = [NSNumber numberWithInt:1];NSLog(@"retainCount = %lu",[number retainCount]);我們看一下打印結果:
結果大的離譜啊,查閱了一下官方的文檔,第一句就是“Do not use this method.”,后面給出了說明,因為Autorelease pool的存在,對于內存的管理會相當復雜,retainCount就不能用作調試內存時的依據了。
總結
以上是生活随笔為你收集整理的iOS开发——MRC(手动内存管理)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 问题1:修改日志文件(redo log)
- 下一篇: android动态设置textview的