iOS開發-記憶體管理

来源:http://www.cnblogs.com/fcug/archive/2017/01/15/6286503.html
-Advertisement-
Play Games

記憶體管理 對於這篇呢,其實現在都是ARC模式,正常狀態下基本不用我們去手動釋放記憶體,所以如果不是要面試呀、裝逼或者扎實功底的,就先別看了或者瞭解下即可,因為像面試時,有些面試官想看你的基礎時,就有些人會問,現在工作基本不會用到。 學習目標 1. 掌握記憶體管理的原理 2. 掌握手動記憶體管理 1.需要理 ...


記憶體管理

對於這篇呢,其實現在都是ARC模式,正常狀態下基本不用我們去手動釋放記憶體,所以如果不是要面試呀、裝逼或者扎實功底的,就先別看了或者瞭解下即可,因為像面試時,有些面試官想看你的基礎時,就有些人會問,現在工作基本不會用到。

 

學習目標

1. 掌握記憶體管理的原理

2. 掌握手動記憶體管理

 

===============================================

1.需要理解的知識

1.1記憶體管理

1.1.1 C的記憶體管理,以及麻煩之處

char *p = (char *)malloc(100*sizeof (char)); 

這是C的動態記憶體分配,我們手動跟系統申請了100個位元組的記憶體;或者說系統在堆里開闢了100個位元組的空間,並將這個空間的首地址返回給指針變數p。

strcpy(p,"Hello World!");

將字元串拷貝給指針變數p指向的記憶體空間。

puts(p);

將p指針指向的記憶體空間里的字元串列印出來。

free(p);

使用完成後,手動跟系統釋放記憶體空間;或者說系統回收空間。

如上就是C里簡單的記憶體管理。

C的記憶體管理,我們手動申請,手動釋放。這樣來看,我們只需要註意兩個問題就好了:

1,申請記憶體,使用完成後需要釋放,如果不釋放會造成記憶體泄露。

2,不能多次釋放,如果多次釋放,則會崩潰。

但是,如果項目比較複雜,需要有幾十上百號人一起分工完成,就很容易出現問題。

比方說我們開闢了一塊記憶體空間,里存放了一塊很有用的數據。但是,這個數據不只有我在這一塊代碼里用,甚至有多個人,在程式的多個地方使用。這樣造成的結果就是,就算我使用完成這塊記憶體,我也不能去釋放他,因為我不能確定,別人在別的地方是否還需要使用這塊記憶體。記憶體泄露在所難免了。

 

 

 

 

OC的記憶體管理:

1.1.2 引用計數(retainCount)

對於一塊動態申請的記憶體,有一個人(指針)使用,就給這個記憶體的計數器加1,使用完成後,就給這個計數器減1,當這個記憶體的引用計數為0了,我們再釋放他,這樣,上面的問題就解決了。OC,就是使用引用計數這種方式來管理記憶體的。

 

 

 

1.1.3 記憶體管理的黃金法則

對於引用計數來說,有一套記憶體管理的黃金法則:

The basic rule to apply is everything that increases the reference counter with alloc, [mutable]copy[withZone:] or retain is in charge of the corresponding [auto]release.

如果對一個對象使用了alloc、copy、mutablecopy、retain,new,那麼你必須使用

相應的release或者autorelease。

通俗一點的說法就是誰污染誰治理。

 

 

 

            

1.1.4 objective-C的記憶體管理遵守下麵這個簡單的策略:

1.你擁有你創建的對象,也就是說創建的對象(使用alloc,new,copy或者mutalbeCopy等方法)的初始引用計數是1。

2.給對象發送retain消息後,你擁有了這個對象 ,retainCount+1

3.當你不需要使用該對象時,發送release或者autorelease消息放棄這個對象

4.不要對你不擁有的對象發送“放棄”的消息

 

 

 

 

 

1.1.4 MRC和ARC

ARC Automatic Reference Counting,自動引用計數,由xcode,幫我們去管理記憶體。

MRC Manual  Reference Counting,手動引用計數,我們手動管理記憶體。

 

Xcode 5.0  版本以後預設是ARC模式,

 

 

 

 

 

 

 

1.1.5 如何將工程改為MRC

xcode5,工程創建的時候是ARC的,我們如果想要MRC,需要進行如下設置。

選中工程 - target - Bulid Settings -Automatic Reference Counting改為NO。

 

 

 

1.1.6 ARC執行了新的規則

 

● 開發者不能顯示調用dealloc;不能實現和調用retain、release、retainCount和autorelease。

禁止使用@selector(retain),@selector(release)等等。

開發者仍可以實現dealloc方法,如果你想管理資源而不是變數。

ARC中自定義的dealloc方法,不需要調用[super dealloc](其實這樣做就會導致編譯錯誤),編譯器會強制自動鏈接到父類。

開發者仍可以對Core Foundation-style對象,使用CFRetain,CFRelease和其他相關方法。

 

● 開發者不能使用NSAutoreleasePool對象。ARC下使用@autoreleasepool,它比NSAtuoreleasePool更有效率。

 

為了配合手動引用計數,ARC的方法命名有限制:

● 訪問器方法不能已new開頭,反過來就是:開發者不能聲明一個已new開頭的屬性,除非你給你指定一個getter

 

// 不正確 
@property NSString *newTitle;   
    
// 正確 
@property (getter=theNewTitle) NSString *newTitle;  
 

 

 

1.1.7.野指針錯誤形式在Xcode中通常表現為:Thread 1EXC_BAD_ACCESS(code=EXC_I386_GPFLT)錯誤。因為你訪問了一塊已經不屬於你的記憶體。

 

2.需要記住的知識

 

2.1 alloc與release

創建一個Dog類

@interface Dog : NSObject

  @end

  @implementation Dog

  - (void)dealloc

  {

    NSLog(@"dog dealloc");

    [super dealloc];

  }

  @end

 

  delloc里的析構函數,當對象銷毀的時候,會自動調用這個方法,我們在這裡重寫這個方法。

  在main函數里,寫入如下代碼:

   

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     }

    NSLog(@"程式即將退出");

    return 0;

  }

 

  從終端列印信息來看,程式即將退出這條列印之前,已經列印dog dealloc,也就是說在程式運行結束前,dog對象已經銷毀了。這個是ARC,由xcode幫我們管理dog對象。

  將ARC改為MRC,再執行程式,dog對象並沒有銷毀,因為我們現在是手動管理了,我們需要遵守記憶體管理的黃金法則,Dog *dog = [[Dog alloc] init]; 我們需要對dog進行release。將main函數代碼改為如下形式:

int main(int argc, const char * argv[])

{

    @autoreleasepool {

        Dog *dog = [[Dog alloc] init];

     [dog release];

     }

    NSLog(@"程式即將退出");

    return 0;

}

 

再次執行程式,從列印可以看出,dog對象,已經銷毀。這就是黃金法則,我們對dog進行alloc,就要對dog進行release。

註意,release 並不是銷毀對象,讓對象的引用計數減1,當對象的引用計數為0的時候,自動調用dealloc方法,銷毀對象。

 

 

 

2.2 retain與retainCount

retain,將對象進行保留操作,也就是使對象的引用計數加1。

retainCount,列印一個對象的引用計數。

 

 

 

 

 

2.3 類的複合中使用

在上面代碼中,增加Person類

@interface Person : NSObject {

  // 一個人,養了一條狗(持有一條狗)

    Dog *_dog;

  }

  - (void)setDog:(Dog *)dog;

  - (Dog *)dog;

  @end

  @implementation Person

  /* 版本1 (有問題) 人並沒有真正持有狗,如果在main函數里[dog release],讓dog的引用計數減1,就變為0,dog就銷毀了。

    - (void)setDog:(Dog *)dog

  {

    _dog = dog;

  }

    */

 

  /* 版本2 (有問題) 如果人再持有別的狗,就會造成第一條狗得不到釋放,記憶體泄露。

  - (void)setDog:(Dog *)dog

  {

    _dog = [dog retain];

  }

    */

 

  /* 版本3 (有問題) 如果本來持有一條狗,又重新設置這條狗,先進行release,這個時候,很可能dog就銷毀了,然後,就沒法再次retain了。

  - (void)setDog:(Dog *)dog

  {

    [_dog release];

    _dog = [dog retain];

  }

    */

 

  // 版本4 OK!,標準寫法

  - (void)setDog:(Dog *)dog

  {

    if (_dog != dog) {

         [_dog release];

          _dog = [dog retain];

      }

  }

 

  - (Dog *)dog

  {

    return _dog;

  }

 

  - (void)dealloc

  {

    NSLog(@"person dealloc");

  // 人在銷毀的時候,一併將持有的dog對象銷毀

    [_dog release];

    [super dealloc];

  }

 

 

 

 

//MRC:

黃金法則:

只要使用了alloc/retain/copy/mutableCopy,new, 創建了對象

那麼就必須使用release進行釋放,

———總結一句話就是:誰創建,誰負責釋放

retain   —  使對象的引用計數+1, 如果指針需要去持有這個對象

需要使用retain

retainCount:  返回對象的引用計數值 

release :  — 使對象的引用計數 -1, 而不是釋放對象

dealloc:對象銷毀的時候(也就是retainCount為0的時候)自動調用這個方法

 

 

 

 

 

MRC:

2.4 @property retain,assign,copy展開

2.4.1 retain展開

如上代碼里,Person的setter和getter方法,也可以用property,寫成如下形式

@property (nonatomic, retain) Dog *dog;

             則會展開如下:

- (void)setDog:(Dog *)dog

{

  if (_dog != dog)

  {

    [_dog release];

    _dog = [dog retain];

  }

}

 

 

 

2.4.2 assign展開

  //簡單數據類型 ,OC的記憶體管理對於簡單的數據類型 int\float…, 

   

@property (nonatomic, assign) Dog *dog;,assign是直接賦值,則會展開如下:

- (void)setDog:(QFDog *)dog

{

  _dog = dog;

}

 

 

 

 

 

  2.4.3 copy展開  , 複製一份原來的對象

 

//copy 多用於字元串

@property (nonatomic, copy)NSString *name;

           展開如下:

- (void)setName:(NSString *)name

{

  if (_name != name)

  {

    [_name release];

    _name = [name copy];

  }

}

 

 

 

 

 

 

 

 

2.4 字元串記憶體管理

  2.4.1 字元串的記憶體管理

  // 對於字元串而言,非常不遵守黃金法則! (如果從字元串的引用計數來看,亂七八糟!) 這隻是一個表象! 其實內部還是遵循的!!

        // 我們要做的是,我們依舊遵守我們的黃金法則!

       

因此,如果是NSString,我們的property格式寫成如下: @property (nonatomic, copy) NSString *name;

 

2.4.2 copy和mutableCopy

       

 

 

2.5 數組的記憶體管理

 

 

 

 

結論

  1)當我們創建數組的時候,數組會對每個對象進行引用計數加1

  2)當數組銷毀的時候,數組會對每個對象進行引用計數減1

  3)當我們給數組添加對象的時候,會對對象進行引用計數加1

  4)當我們給數組刪除對象的時候,會對對象進行引用計數減1

  總之,誰污染誰治理,管好自己就可以了(數組內部也遵守記憶體管理)。

 

 

 

 

2.6 autorelease與autoreleasepool

在main函數里寫如下代碼:

int main(int argc, const char * argv[])

  {

    @autoreleasepool {

           Dog *dog = [[Dog alloc] init];

      //dog並沒有馬上銷毀,而是延遲銷毀,將dog對象的擁有權交給了autoreleasepool

      [dog autorelease];

      //這個是可以列印的,因為列印完dog的引用計數後,dog對象才銷毀

      NSLog(@"retainCount = %lu",dog.retainCount);

     }

    NSLog(@"程式即將退出");

    return 0;

  }

 

  autoreleasepool相當於一個數組,如果哪個對象發送autorelease消息,實際將對象的擁有權交給了autoreleasepool;當autoreleasepool銷毀的時候,autoreleasepool里持有的對象都發送一個release消息。

 

2.7 加方法的記憶體管理 

  我們用加方法創建的對象,不用我們release,是因為類內部的實現使用了autorelease,延遲釋放

  在Dog類的聲明裡增加一個加方法

  + (id)dog;

  在Dog類的實現里進行實現

  + (id)dog

  {

註意,這裡不要寫成release,如果是release,那麼剛創建就銷毀了,使用autorelease,使得將對象的擁有權交給了自動釋放池,只要自動釋放池沒有銷毀,dog對象也就不會銷毀。

return [[[Dog alloc] init] autorelease];

  }

 

 

2.8  對於自動記憶體釋放簡單總結一下:

  1.                1 autorelease方法不會改變對象的引用計數器,只是將這個對象放到自動釋放池中;
  2. 自動釋放池實質是當自動釋放池銷毀後調用對象的release方法,不一定就能銷毀對象(例如如果一個             對象的引用計數器>1則此時就無法銷毀);
  3. 由於自動釋放池最後統一銷毀對象,因此如果一個操作比較占用記憶體(對象比較多或者對象占用資源比較多),最好不要放到自動釋放池或者考慮放到多個自動釋放池;
  4. ObjC中類庫中的靜態方法一般都不需要手動釋放,內部已經調用了autorelease方法;

 

 

=====================================

 

ARC模式下的關鍵字:

__strong/__weak/__unsafe_unretain

 

開發者需要正確修飾變數。使用下麵的格式來修飾變數聲明。

 

             類名*  修飾  變數名

             例如:

 

       MyClass * __weak myWeakReference;   
       MyClass * __unsafe_unretained myUnsafeReference; 

 

 

對應的@property 參數分別為

strong/weak/unsafe_unretain

 

__strong : 強引用,相當於MRC下的retain,指針對對象具有決定的占

                            有,預設情況。

__weak :     弱引用,指針對對象不具有決定的占有,相當於MRC下的

      assign,對象釋放後,指針賦值為nil。

__unsafe_unretain:弱引用,指針對對象不具有決定的占有,相當於MRC下的assign,對象釋放後,指針為懸垂指針(不會賦值為nil),可以會出現野指針,不建議使用。

 

@property(nonatomic, strong) xxx

//set 類似於 retain 展開  [name retain]

@property(nonatomic, weak)  xxx

//類似於 assign

@property(nonatomic, unsafe_unretain) xxx

//類似於 assign

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在做下拉框,本來想用spinner,可是spinner達不到項目要求,跟同學同事問了一圈,都在用popwindow, 網上看了一下,popwindow挺簡單的,可定製性挺強的,符合我的要求,所以,借鑒網上看的代碼,自己擼了一 遍。寫篇博客以防忘記。 首先,先寫個自定義佈局,代碼如下 <?xml ...
  • 這裡是放代碼的地方嗎? ...
  • 設計師很多時候會有一像素線的需求,怎麼去實現有下麵幾種方法。 一、 Image 1. 資源圖片 找設計師要個圖片,用UIImageView 或者 CALayer的方式顯示出來 2. CoreGraphics繪製 用CoreCraphics的方式畫一個 UIImage,然後用1的方法展示出來 此類方法 ...
  • 此開源框架官網地址:https://github.com/astuetz/PagerSlidingTabStrip 可以理解為配合ViewPager使用的互動式頁面指示器控制項。 話不多說,先上效果圖: 為了演示其中的pstsIndicatorHeight與pstsUnderlineHeight 的區 ...
  • 前兩天群里有兄弟在吐槽,做遠程推送的時候:老闆要求APP桌面圖標的右上角顯示紅色未讀數字(數字角標)要精準,有多少未讀通知就顯示數字幾;但是後臺的弟兄在發送推送通知的時候,每次的角標是1,然後要移動端這邊自己去把這個未讀數字去累加,然後顯示在APP上;並且後臺非常固執的認為這個累加未讀消息數量是在移 ...
  • 開發小程式所需的基本技能 關於小程式的介紹和使用場景這裡不作介紹,這個系列的文章會一步一步地帶領大家快速地學習和掌握小程式的開發。 關於還沒有接觸過小程式的開發者來說,最關心的問題無非就是,開發小程式,我需要掌握哪些技能? 從我學習小程式的經驗中得出,開發小程式的基本要求: 如果你是一名已經工作1年 ...
  • 演示環境:Mac OSX10.12.2 Xcode8 先瞄一眼最終成果~ 1.JDK,DBMS(演示環境使用Mysql) 2.創建sonar資料庫和用戶 3. "下載sonar" 演示環境使用v6.1,下載後解壓到 /usr/local/sonar/ 4. "下載sonar runner" :演示環 ...
  • 目錄操作和文件管理 學習目標 1.理解單例 2.掌握NSFileManager類常用的文件管理操 3.掌握NSFileHandle類常用的文件數據操作 4.瞭解NSData類的常用操作 5.掌握Plist文件讀寫 —————————————————————— 通常程式在運行中或者程式結束之後,需要保 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...