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

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

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

Objective-C 和 Swift 語(yǔ)言的內(nèi)存管理方式都是基于引用計(jì)數(shù)「Reference Counting」的,引用計(jì)數(shù)是一個(gè)簡(jiǎn)單而有效管理對(duì)象生命周期的方式。引用計(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è)新對(duì)象時(shí),他的引用計(jì)數(shù)為1;

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

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

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

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

舉個(gè)例子:

新建工程,Xcode 默認(rèn)開(kāi)啟的是 ARC,我們這里針對(duì)「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"]; // 僵尸對(duì)象,向野指針發(fā)送消息會(huì)報(bào)錯(cuò)(EXC_BAD_ACCESS)
  
  return YES;
}

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

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

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

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

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

1.2 什么是僵尸對(duì)象、野指針、空指針呢?

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

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

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

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

從上面簡(jiǎn)單例子,我們還看不出引用計(jì)數(shù)真正的用處,因?yàn)樵搶?duì)象的生命周期只是在一個(gè)方法內(nèi)。在真實(shí)的應(yīng)用場(chǎng)景中,我們?cè)诜椒▋?nèi)使用臨時(shí)對(duì)象,通常不需要修改他的引用計(jì)數(shù),只需要在方法返回前銷(xiāo)毀對(duì)象就可以了。

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

舉個(gè)例子:

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

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

對(duì)于以上情況有兩種做法:

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

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

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

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

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

舉個(gè)例子:

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

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

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

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

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

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

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

舉個(gè)例子:

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

可以選擇「Product」下的「Profile」來(lái)打開(kāi)「Instruments」工具集。

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

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

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

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

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

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

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

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

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

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

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

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

相關(guān)文章

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

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

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

    簡(jiǎn)單說(shuō)說(shuō)iOS之WKWebView的用法小結(jié)

    iOS8.0之后我們使用 WebKit框架中的WKWebView來(lái)加載網(wǎng)頁(yè)。這篇文章主要介紹了簡(jiǎn)單說(shuō)說(shuō)iOS之WKWebView的用法小結(jié),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    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開(kāi)發(fā)教程之WKWebView與JS的交互

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

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

    iOS仿簡(jiǎn)書(shū)、淘寶等App的View彈出效果

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

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

    iPhone X出來(lái)之后,關(guān)于劉海的各種適配成了程序員們首要考慮的問(wèn)題,下面這篇文章主要給大家介紹了關(guān)于iPhoneX 各種適配的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來(lái)一起看看吧。
    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ù)語(yǔ)來(lái)講,這叫做“給某個(gè)對(duì)象發(fā)送某條消息”。文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。
    2018-03-03
  • iOS中視頻播放器的簡(jiǎn)單封裝詳解

    iOS中視頻播放器的簡(jiǎn)單封裝詳解

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

    iOS10 Xcode8開(kāi)發(fā)適配問(wèn)題及解決方案

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

最新評(píng)論