日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS开发——MRC(手动内存管理)

發布時間:2024/3/12 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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段)。

    關于如何查看一個對象在堆區 / 棧區

  • 你初始化方法以 new, alloc, retain,copy 開頭都是在堆區,也包括被引用計數管理的對象。常量這些就會在棧區。簡單一點,除去 NSString *aString = @“aaa” 這種編譯時會轉換為常量其它的 Objective-C 對象,理論上來說,都是在堆區。此外,block 也是個例外,具體的可以去了解內存管理相關的知識點。
  • 如果是在方法執行過程當中,定義在本地的原生類型(或者說值類型)。那么肯定它是在棧上。當函數執行結束時直接銷毀。而其它的引用類型(或者oc中的interface)都是在堆上創建的,由ARC負責清理
  • 一般情況下你可以打印出地址,判斷他是在堆還是棧內存中,棧是高地址往下,堆是低地址往上,一般看到0x7fff…這種的肯定是是在棧中的,要注意一點的是TEXT段,像NSString這種基本放在TEXT段中,這種地址比堆地址更低,也很好區分;你可以去看一下內存是怎么劃分的;
  • 需要知道的知識

    在學習MRC之前除了要了解計算機關于內存的分配區域之外,還需要了解一些知識,具體如下:

  • 什么樣的數據需要進行內存管理?任何繼承了NSObject的對象都需要進行內存管理,而那些非對象類型的數據比如:int、char、double、struct等等就不需要進行內存管理。
  • 繼承了NSObject的對象存儲在操作系統的堆里,堆一般由程序員分配釋放,程序結束時可能有OS回收。
  • 非OC對象一般放在操作系統的棧里,棧由操作系統自動分配釋放,存放函數的參數值,局部變量的值等。其操作方式類似于數據結構中的棧(先進后出)。
  • 另外現在我們的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。
    假如我將代碼改成如下:

    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]);[obj autorelease];NSLog(@"%lu",(unsigned long)[obj retainCount]);return 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(手动内存管理)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。