欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

IOS中內(nèi)存管理那些事

 更新時(shí)間:2015年11月14日 12:24:05   投稿:hebedich  
這篇文章主要介紹了IOS中內(nèi)存管理那些事的相關(guān)資料,需要的朋友可以參考下

Objective-C 和 Swift 語言的內(nèi)存管理方式都是基于引用計(jì)數(shù)「Reference Counting」的,引用計(jì)數(shù)是一個(gè)簡單而有效管理對象生命周期的方式。引用計(jì)數(shù)分為手動(dòng)引用計(jì)數(shù)「ARC: AutomaticReference Counting」和自動(dòng)引用計(jì)數(shù)「MRC: Manual Reference Counting」,現(xiàn)在都是用 ARC 了,但是我們還是很有必要了解 MRC。

1. 引用計(jì)數(shù)的原理是什么?

當(dāng)我們創(chuàng)建一個(gè)新對象時(shí),他的引用計(jì)數(shù)為1;

當(dāng)有一個(gè)新的指針指向這個(gè)對象時(shí),他的引用計(jì)數(shù)就加1;

當(dāng)對象關(guān)聯(lián)的某個(gè)指針不再指向他時(shí),他的引用計(jì)數(shù)就減1;

當(dāng)對象的引用計(jì)數(shù)為0時(shí),說明此對象不再被任何指針指向,這時(shí)我們就可以將對象銷毀,回收內(nèi)存。

由于引用計(jì)數(shù)簡單有效,除了 Objective-C 語言外,Microsoft 的 COM「Component Object Model」、C++11(基于引用計(jì)數(shù)的智能指針 share_prt)等語言也提供了基于引用計(jì)數(shù)的內(nèi)存管理方式。

舉個(gè)例子:

新建工程,Xcode 默認(rèn)開啟的是 ARC,我們這里針對「AppDelegate.m」文件使用 MRC,進(jìn)行以下配置:

選擇目標(biāo)工程,然后在「Build Phases」的「Compile Sources」下的「AppDelegate.m」文件配置編譯器參數(shù)「Compiler Flags」值為「-fno-objc-arc」

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  NSObject *objO = [NSObject new];
  NSLog(@"Reference Count: %lu", (unsigned long)[objO retainCount]); // 1
  NSObject *objB = [objO retain];
  NSLog(@"Reference Count: %lu", (unsigned long)[objO retainCount]); // 2
  [objO release];
  NSLog(@"Reference Count: %lu", (unsigned long)[objO retainCount]); // 1
  [objO release];
  NSLog(@"Reference Count: %lu", (unsigned long)[objO retainCount]); // 1
  
  [objO setValue:nil forKey:@"test"]; // 僵尸對象,向野指針發(fā)送消息會(huì)報(bào)錯(cuò)(EXC_BAD_ACCESS)
  
  return YES;
}

Xcode 默認(rèn)不會(huì)監(jiān)控僵尸對象,這里我們配置開啟他,然后就可以看到具體的跟蹤信息了:

也可以通過選擇「Product」下的「Profile」來打開「Instruments」工具集。然后選擇「Zombies」,再單擊右下角的「Choose」按鈕進(jìn)入檢測界面,這時(shí)點(diǎn)擊左上角的「Record」紅色圓點(diǎn)按鈕開始檢測。

1.1 上面例子,為什么最后一次通過 retainCount 獲取的值為1,而不是為0呢?

因?yàn)樵搶ο蟮膬?nèi)存已經(jīng)被回收,我們向一個(gè)被回收的對象發(fā)送 retainCount 消息,他的輸出結(jié)果是不確定的,如果該對象所占內(nèi)存被復(fù)用了,那么就可能造成程序異常崩潰。

而且當(dāng)最后一次執(zhí)行 release 時(shí),系統(tǒng)已經(jīng)知道馬上要回收內(nèi)存了,就沒必要再將 retainCount 減1,因?yàn)椴还軠p不減1,該對象都會(huì)被回收,回收后他所在內(nèi)存區(qū)域(包括 retainCount 值)就沒有意義了。不將retainCount 減1變?yōu)?,可以減少一次內(nèi)存操作,加快對象的回收。

1.2 什么是僵尸對象、野指針、空指針呢?

僵尸對象:所占用內(nèi)存已經(jīng)被回收的對象,僵尸對象不能再使用。

野指針:指向僵尸對象(不可用內(nèi)存)的指針,給野指針發(fā)送消息會(huì)報(bào)錯(cuò)(EXC_BAD_ACCESS)。

空指針:沒有指向任何對象的指針(存儲(chǔ)的是 nil、NULL),給空指針發(fā)送消息不會(huì)報(bào)錯(cuò);空指針的一個(gè)經(jīng)典使用場景就是在開發(fā)中獲取服務(wù)器 API 數(shù)據(jù)時(shí),轉(zhuǎn)換野指針為空指針,避免發(fā)送消息報(bào)錯(cuò)。

2. 為什么需要引用計(jì)數(shù)?

從上面簡單例子,我們還看不出引用計(jì)數(shù)真正的用處,因?yàn)樵搶ο蟮纳芷谥皇窃谝粋€(gè)方法內(nèi)。在真實(shí)的應(yīng)用場景中,我們在方法內(nèi)使用臨時(shí)對象,通常不需要修改他的引用計(jì)數(shù),只需要在方法返回前銷毀對象就可以了。

然而,引用計(jì)數(shù)真正派上用場的場景是在面向?qū)ο蟮某绦蛟O(shè)計(jì)架構(gòu)中,用于對象之間傳遞和共享數(shù)據(jù)。

舉個(gè)例子:

假如對象 A 生成了一個(gè)對象 O,需要調(diào)用對象 B 的某個(gè)方法,將對象 O 作為參數(shù)傳遞過去。

在沒有引用計(jì)數(shù)的情況下,一般內(nèi)存管理的原則是「誰申請誰釋放」,那么對象 A 就需要在對象 B 不再需要對象 O 的時(shí)候,將對象 O 銷毀。但對象 B 可能臨時(shí)用一下對象 O,也可以覺得他重要,將他設(shè)置為自己的一個(gè)成員變量,在這種情況下,什么時(shí)候銷毀對象 O 就成了一個(gè)難題了。

對于以上情況有兩種做法:

(1)對象 A 在調(diào)用完對象 B 的某個(gè)方法之后,馬上銷毀參數(shù)對象 O,然后對象 B 需要將對象 O 復(fù)制一份,生成另一個(gè)對象 O2,同時(shí)自己來管理對象 O2 的生命周期。但是這種做法有一個(gè)很大的問題,就是他帶來更多的內(nèi)存申請、復(fù)制、釋放的工作。本來可以復(fù)用的對象,因?yàn)椴环奖愎芾硭纳芷?,就簡單地把他銷毀,又重新構(gòu)造一份一樣的,實(shí)在太影響性能。

(2)對象 A 只負(fù)責(zé)生成對象 O,之后就由對象 B 負(fù)責(zé)完成對象 O 的銷毀工作。如果對象 B 只是臨時(shí)用一下對象 O,就可以用完后馬上銷毀,如果對象 B 需要長時(shí)間使用對象 O,就不銷毀他。這種做法看似解決了對象復(fù)制的問題,但是他強(qiáng)烈依賴于 A 和 B 兩個(gè)對象的配合,代碼維護(hù)者需要明確地記住這種編程約定。而且,由于對象 O 的生成和釋放在不同對象中,使得他的內(nèi)存管理代碼分散在不同對象中,管理起來也很費(fèi)勁。如果這個(gè)時(shí)候情況更加復(fù)雜一些,例如對象 B 需要再向?qū)ο?C 傳遞參數(shù)對象 O,那么這個(gè)對象在對象 C 中又不能讓對象 C 管理。所以這種方法帶來的復(fù)雜度更高,更加不可取。

引用計(jì)數(shù)的出現(xiàn)很好地解決這個(gè)問題,在參數(shù)對象 O 的傳遞過程中,哪些對象需要長時(shí)間使用他,就把他的引用計(jì)數(shù)加1,使用完就減1。所有對象遵守這個(gè)規(guī)則,對象的生命周期管理就可以完全交給引用計(jì)數(shù)了。我們也可以很方便地享受到共享對象帶來的好處。

2.1 什么是循環(huán)引用「Reference Cycles」問題,怎么解決呢?

引用計(jì)數(shù)這種內(nèi)存管理方式雖然簡單,但有一個(gè)瑕疵就是他不能自動(dòng)解決循環(huán)引用的問題。

舉個(gè)例子:

對象 A 和對象 B 相互引用對方作為自己的成員變量,只有當(dāng)自己銷毀時(shí),才將自己的成員變量的引用計(jì)數(shù)減1,因?yàn)閷ο?A 和對象 B 的銷毀相互依賴,這樣就造成我們所說的循環(huán)引用問題了。

循環(huán)引用會(huì)導(dǎo)致即使外界已經(jīng)沒有任何指針能夠訪問他們了,但是他們所占資源仍然無法釋放的情況。

解決循環(huán)引用問題主要有兩種方法:

(1)明確知道哪里存在循環(huán)引用,合理時(shí)機(jī)主動(dòng)斷開環(huán)中的一個(gè)引用,使得對象得以回收。這種方法不常用,因?yàn)樗蕾囬_發(fā)人員自己手工顯式控制,相當(dāng)于回到以前「誰申請誰釋放」的內(nèi)存管理年代。

(2)使用弱引用「Weak Reference」,「weak」「__weak」類型,這種方法常用。弱引用雖然持有對象,但是并不增加他的引用計(jì)數(shù)。弱引用的一個(gè)經(jīng)典使用場景就是委托代理「delegate」協(xié)議模式。

2.2 Xcode 中有什么工具可以檢測循環(huán)引用嗎?

在 Xcode 中有「Instruments」工具集可以很方便地檢測循環(huán)引用。

舉個(gè)例子:

- (void)viewDidLoad {
  [super viewDidLoad];
  
  NSMutableArray *mArrFirst = [NSMutableArray array];
  NSMutableArray *mArrSecond = [NSMutableArray array];
  [mArrFirst addObject:mArrSecond];
  [mArrSecond addObject:mArrFirst];
}

可以選擇「Product」下的「Profile」來打開「Instruments」工具集。

然后選擇「Leaks」,再單擊右下角的「Choose」按鈕進(jìn)入檢測界面,這時(shí)點(diǎn)擊左上角的「Record」紅色圓點(diǎn)按鈕開始檢測。

3. Core Foundation 對象的內(nèi)存管理

ARC 是編譯器特性,他不是運(yùn)行時(shí)特性,更不是垃圾回收器「GC」。

ARC 能夠解決 iOS 開發(fā)中90%的內(nèi)存管理問題,但是另外10%的內(nèi)存管理問題是需要開發(fā)人員自己處理的,這主要是與底層 Core Foundation 對象交互的部分,底層 Core Foundation 對象由于不在 ARC 的管理下,所以需要自己維護(hù)這些對象的引用計(jì)數(shù)。

實(shí)際上 Core Foundation 對象使用的 CFRetain 和 CFRelease 方法,可以認(rèn)為與 Objective-C 對象的 retain 和 release 方法等價(jià),所以我們可以以 MRC 的方式進(jìn)行類似管理。

3.1 在 ARC 中,通過什么方式可以把 Core Foundation 對象轉(zhuǎn)換為 Objective-C 對象呢?

轉(zhuǎn)換的過程,其實(shí)是告訴編譯器,對象的引用計(jì)數(shù)如何調(diào)整。

這里我們可以使用橋接「bridge」相關(guān)關(guān)鍵字來進(jìn)行轉(zhuǎn)換工作,以下是這些(雙下劃線)關(guān)鍵字的說明:

(1)__bridge:只做類型轉(zhuǎn)換,不修改相關(guān)對象的引用計(jì)數(shù),原來的 Core Foundation 對象在不用時(shí),需要調(diào)用 CFRelease 方法。

(2)__bridge_retained:類型轉(zhuǎn)換后,將相關(guān)對象的引用計(jì)數(shù)加1,原來的 Core Foundation 對象在不用時(shí),需要調(diào)用 CFRelease 方法。

(3)__bridge_transfer:類型轉(zhuǎn)換后,將相關(guān)對象的引用計(jì)數(shù)交給 ARC 管理,原來的 Core Foundation 對象在不用時(shí),不需要調(diào)用 CFRelease 方法。

我們根據(jù)具體的業(yè)務(wù)邏輯,合理使用上面的三種轉(zhuǎn)換關(guān)鍵字,就可以解決Core Foundation 對象 與 Objective-C 對象相對轉(zhuǎn)換的問題了。

相關(guān)文章

  • 詳解iOS App中UIPickerView滾動(dòng)選擇欄的添加方法

    詳解iOS App中UIPickerView滾動(dòng)選擇欄的添加方法

    UIPickerView組件在應(yīng)用中選擇地區(qū)等方面的運(yùn)用非常常見,能夠提供多列的選擇項(xiàng),下買呢我們就來詳解iOS App中UIPickerView滾動(dòng)選擇欄的添加方法
    2016-05-05
  • 簡單說說iOS之WKWebView的用法小結(jié)

    簡單說說iOS之WKWebView的用法小結(jié)

    iOS8.0之后我們使用 WebKit框架中的WKWebView來加載網(wǎng)頁。這篇文章主要介紹了簡單說說iOS之WKWebView的用法小結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • 分享一個(gè)關(guān)于Storyboard 跳轉(zhuǎn)與傳值

    分享一個(gè)關(guān)于Storyboard 跳轉(zhuǎn)與傳值

    近日不忙,給大家分享一個(gè)關(guān)于storyboard跳轉(zhuǎn)傳值的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2015-12-12
  • iOS開發(fā)教程之WKWebView與JS的交互

    iOS開發(fā)教程之WKWebView與JS的交互

    這篇文章主要給大家介紹了關(guān)于iOS開發(fā)教程之WKWebView與JS的交互的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對各位iOS開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • iOS仿簡書、淘寶等App的View彈出效果

    iOS仿簡書、淘寶等App的View彈出效果

    這篇文章主要為大家詳細(xì)介紹了iOS仿簡書、淘寶等App的View彈出效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • iPhoneX 各種適配記錄筆記(超全面)

    iPhoneX 各種適配記錄筆記(超全面)

    iPhone X出來之后,關(guān)于劉海的各種適配成了程序員們首要考慮的問題,下面這篇文章主要給大家介紹了關(guān)于iPhoneX 各種適配的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-12-12
  • IOS封裝自定義布局的方法

    IOS封裝自定義布局的方法

    這篇文章主要介紹了IOS封裝自定義布局的方法,需要的朋友可以參考下
    2016-01-01
  • iOS消息發(fā)送和轉(zhuǎn)發(fā)示例詳解

    iOS消息發(fā)送和轉(zhuǎn)發(fā)示例詳解

    這篇文章主要給大家介紹了關(guān)于iOS消息發(fā)送和轉(zhuǎn)發(fā)的相關(guān)資料,用Objective-C的術(shù)語來講,這叫做“給某個(gè)對象發(fā)送某條消息”。文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • iOS中視頻播放器的簡單封裝詳解

    iOS中視頻播放器的簡單封裝詳解

    要實(shí)現(xiàn)封裝視頻播放器,首先需要實(shí)現(xiàn)視頻播放器,然后再去考慮怎樣封裝可以讓以后自己使用起來方便快捷。iOS9之前可以使用MediaPlayer來進(jìn)行視頻的播放,iOS9之后系統(tǒng)推薦使用AVFoundation框架實(shí)現(xiàn)視頻的播放。下面通過本文來看看詳細(xì)的介紹吧。
    2016-10-10
  • iOS10 Xcode8開發(fā)適配問題及解決方案

    iOS10 Xcode8開發(fā)適配問題及解決方案

    前段時(shí)間升級(jí)了Xcode8,整體來說對OC的影響不大,但是還是會(huì)有這樣那樣的問題,下面小編給大家總結(jié)了遇到的適配問題及解決方案,對xcode8 ios10適配問題感興趣的朋友一起看看吧
    2016-11-11

最新評(píng)論