成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS 開發(fā)—探秘 Block 原理

移動(dòng)開發(fā) iOS
很多block原理性的文章都比較老,里面講的一些知識(shí)已經(jīng)過時(shí),這里用新版的iOS SDK再梳理一遍block原理,也是和大家一起對(duì)已有知識(shí)做一次復(fù)習(xí)。

1.概述

在iOS開發(fā)中,block大家用的都很熟悉了,是iOS開發(fā)中閉包的一種實(shí)現(xiàn)方式,可以對(duì)一段代碼邏輯進(jìn)行封裝,使其可以像數(shù)據(jù)一樣被傳遞、存儲(chǔ)、調(diào)用,并且可以保存相關(guān)的上下文狀態(tài)。

很多block原理性的文章都比較老,里面講的一些知識(shí)已經(jīng)過時(shí),這里用新版的iOS SDK再梳理一遍block原理,也是和大家一起對(duì)已有知識(shí)做一次復(fù)習(xí)。

2.內(nèi)存布局

block本質(zhì)上可以理解為結(jié)構(gòu)體,對(duì)于結(jié)構(gòu)體的內(nèi)存布局,先用一張圖來表示一下,圖中字段順序按照布局的先后順序:

  • isa:block也有isa,從內(nèi)存結(jié)構(gòu)上也屬于對(duì)象,isa指向的是block的類對(duì)象,類對(duì)象例如__NSMallocBlock__,后續(xù)文章會(huì)講到;
  • flags:用于存儲(chǔ)一些標(biāo)志位信息,例如是否捕獲外部變量;
  • reserved:系統(tǒng)保留字段,后續(xù)可能會(huì)用于一些編譯優(yōu)化標(biāo)志位,或者存儲(chǔ)一些臨時(shí)變量的處理;
  • invoke:函數(shù)指針,指向了block要執(zhí)行的函數(shù)地址,也就是block代碼塊對(duì)應(yīng)的函數(shù)地址;
  • descriptor(現(xiàn)在叫desc):指向block_desc_0,包含block大小、捕獲的外部變量布局信息、增加引用計(jì)數(shù)和銷毀的相關(guān)函數(shù)指針;
  • variables:block捕獲的外部變量。

圖片圖片

3.類型

由于block也是對(duì)象,可以通過class方法獲取到其類型,也就是類對(duì)象。block有下面三種類型:

  • __NSGlobalBlock__,沒有訪問auto變量的block,訪問static變量是沒問題的。這種類型的變量并沒有什么意義,如果不需要用到auto變量,寫成方法就可以滿足需求;
  • __NSStackBlock__,在MRC環(huán)境下,訪問了auto變量,會(huì)默認(rèn)被放在棧區(qū)。需要手動(dòng)copy到堆區(qū),ARC環(huán)境下會(huì)在訪問auto變量后,會(huì)自動(dòng)拷貝到堆區(qū);
  • __NSMallocBlock__,由開發(fā)者自己管理內(nèi)存,不會(huì)由系統(tǒng)來釋放。

block的分配主要是在三個(gè)區(qū)域,堆區(qū)、棧區(qū)、全局區(qū),全局區(qū)的數(shù)據(jù)存儲(chǔ)在數(shù)據(jù)段。

block在不同的場(chǎng)景會(huì)存在不同的內(nèi)存區(qū)域中,在MRC中創(chuàng)建一個(gè)block首先是在__NSStackBlock__內(nèi)存中的,然后我們使用copy方法將block拷貝到__NSMallocBlock__內(nèi)存中進(jìn)行內(nèi)存管理。后來在ARC中系統(tǒng)已經(jīng)幫我們做好了copy的操作,創(chuàng)建的block會(huì)自動(dòng)copy到__NSMallocBlock__內(nèi)存中,堆區(qū)的block也有引用計(jì)數(shù)的概念。如果這個(gè)block中沒有用到任何外部參數(shù),系統(tǒng)會(huì)將這個(gè)block存放在__NSGlobalBlock__內(nèi)存中。

圖片圖片

并且block也有繼承關(guān)系,以下面TestBlock的實(shí)例來說,其父類是__NSGlobalBlock__,所有block的父類是NSBlock,并且NSBlock繼承自NSObject類。在更早一些的iOS系統(tǒng)中,__NSGlobalBlock__和NSBlock之間,還會(huì)有一層__NSGlobalBlock的關(guān)系(后面沒有下劃線)。

圖片圖片

4.轉(zhuǎn)換C++

下面,我們通過clang命令將block轉(zhuǎn)為結(jié)構(gòu)體,來分析下其具體實(shí)現(xiàn)。雖然這并不是最終運(yùn)行在iOS系統(tǒng)上的代碼,其等于一種中間表現(xiàn)形式,后續(xù)編譯鏈接優(yōu)化才會(huì)形成運(yùn)行在手機(jī)上的ipa包,但對(duì)于我們了解block的實(shí)現(xiàn)原理有很大幫助。

4.1轉(zhuǎn)換命令

xcrun是Xcode用于查找和執(zhí)行相關(guān)命令行的工具集,可以更好的執(zhí)行clang命令,減少報(bào)錯(cuò)。

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc [源文件路徑] -o [目標(biāo)文件路徑]

clang命令有下面這些關(guān)鍵參數(shù):

  • -fobjc-arc:如果項(xiàng)目是ARC或者ARC和MRC混編的環(huán)境,需要通過此參數(shù)修飾,表示按ARC的方式進(jìn)行轉(zhuǎn)換,如果不需要ARC環(huán)境可以忽略;
  • -x objective-c++:此參數(shù)上面沒用,如果包含Objective++源文件的時(shí)候,需要用到此參數(shù),以確保clang可以區(qū)分OC和C++代碼;
  • -rewrite-objc:告訴clang以C++的方式重寫出來,包含的上層代碼,clang會(huì)以底層代碼的方式進(jìn)行展現(xiàn);
  • [目標(biāo)文件路徑]:非必傳參數(shù),不傳的話默認(rèn)在當(dāng)前目錄生成一個(gè)同名的cpp文件,例如main.m對(duì)應(yīng)main.cpp。

4.2轉(zhuǎn)換示例

下面在main.m中實(shí)現(xiàn)了一個(gè)很簡單的block,并且沒有捕獲任何外部變量,通過clang命令查看C++代碼,觀察block的具體實(shí)現(xiàn)原理。

圖片圖片

轉(zhuǎn)換后將C++源文件拉到最下面,可以看到main函數(shù)以及TestBlock的實(shí)現(xiàn),main函數(shù)中有很多轉(zhuǎn)義代碼,刪掉后梳理邏輯會(huì)更清晰。

圖片圖片

5.結(jié)構(gòu)體

5.1基礎(chǔ)結(jié)構(gòu)

轉(zhuǎn)換后的代碼看著比較復(fù)雜,但我們只看關(guān)鍵信息,__main_block_impl_0構(gòu)造函數(shù)也可以去掉,整理后就是下面三個(gè)結(jié)構(gòu)體。在不包含外部變量和__block的前提下,block結(jié)構(gòu)體各個(gè)字段就這么簡單,關(guān)鍵就是isa、Block_size、FuncPtr這三個(gè)。

圖片圖片

我們也可以打印block結(jié)構(gòu)體相關(guān)字段,但由于block的結(jié)構(gòu)體并沒有聲明在某個(gè).h文件中,所以需要我們講clang轉(zhuǎn)換后的結(jié)構(gòu)體粘到對(duì)應(yīng)的文件中,做顯示聲明。隨后用__bridge的方式,將block對(duì)象橋接為自己聲明的結(jié)構(gòu)體,即可打印對(duì)應(yīng)字段。

圖片圖片

結(jié)構(gòu)體中impl.FuncPtr存儲(chǔ)的就是回調(diào)函數(shù)地址,從地址可以看出是一個(gè)虛擬地址,block結(jié)構(gòu)體都存儲(chǔ)在堆區(qū)。

圖片圖片

5.2調(diào)用部分

看完block結(jié)構(gòu)體的定義,我們來到main函數(shù)中,看block的實(shí)現(xiàn)和調(diào)用轉(zhuǎn)換后是什么樣的。將main函數(shù)中block相關(guān)的轉(zhuǎn)換都去掉,結(jié)果如紅圈部分。本質(zhì)上就是兩步,第一步是調(diào)用__main_block_impl_0的結(jié)構(gòu)體構(gòu)造函數(shù),第二步是調(diào)用結(jié)構(gòu)體的函數(shù)指針。

圖片圖片

第一行main函數(shù)中調(diào)用的構(gòu)造方法,是__main_block_impl_0結(jié)構(gòu)體聲明的C++構(gòu)造函數(shù),因?yàn)槲覀儎?chuàng)建的是一個(gè)最簡單block,可以看到block的存儲(chǔ)區(qū)域是在stack棧區(qū)的。即main函數(shù)調(diào)用完,block生命周期就會(huì)結(jié)束。

圖片圖片

__main_block_impl_0構(gòu)造函數(shù)有兩個(gè)參數(shù),第一個(gè)紅圈部分就是傳入函數(shù)指針地址,函數(shù)對(duì)應(yīng)的就是block內(nèi)部的實(shí)現(xiàn)代碼。第二個(gè)參數(shù)是__main_block_desc_0_DATA結(jié)構(gòu)體,其定義為__main_block_desc_0,并且默認(rèn)實(shí)現(xiàn)第一個(gè)參數(shù)傳0,第二個(gè)參數(shù)是block結(jié)構(gòu)體的大小,結(jié)構(gòu)體為__main_block_impl_0 block自身的結(jié)構(gòu)體大小。第三個(gè)參數(shù)有默認(rèn)值,可以不傳。

圖片圖片

__main_block_desc_0結(jié)構(gòu)體是一種緊湊型的寫法,在聲明__main_block_desc_0結(jié)構(gòu)體后,緊接著聲明了一個(gè)名為__main_block_desc_0_DATA的變量,變量類型為靜態(tài)變量,并且實(shí)現(xiàn)了初始化相關(guān)代碼。

圖片圖片

在執(zhí)行block的代碼位置,可以看到并不是block->impl.FuncPtr的方式調(diào)用,而是直接block->FuncPtr的方式調(diào)用,中間少了一步。

嚴(yán)謹(jǐn)些來說應(yīng)該加上impl,但不加也不會(huì)出問題。這是因?yàn)椋绻次磩h除轉(zhuǎn)換代碼的原始clang代碼,可以看到block是被轉(zhuǎn)換為__block_impl的,也就是說被當(dāng)做__block_impl看待的。如果再結(jié)合__main_block_impl_0的結(jié)構(gòu)體定義來看,__block_impl在成員變量的第一位,所以訪問FuncPtr是沒有問題的,只要不訪問Desc就是可以的。

6.外部變量

6.1值類型

如果在block的調(diào)用中加一個(gè)外部變量,那結(jié)構(gòu)體將會(huì)是怎樣的?

圖片圖片

通過clang命令可以可以看到,轉(zhuǎn)換后的__main_block_impl_0中增加了一個(gè)同名字段,這很簡單沒必要過多解釋。在__main_block_impl_0構(gòu)造函數(shù)中傳入,通過冒號(hào)后的初始化列表對(duì)value參數(shù)進(jìn)行初始化。

圖片圖片

后面?zhèn)鲄⒑褪褂茫投际墙Y(jié)構(gòu)體賦值和取值邏輯,很簡單。

圖片圖片

6.2值傳遞

下面這種寫法,在block的使用中很容易踩坑。在block中使用value參數(shù),并且打印value參數(shù),發(fā)現(xiàn)結(jié)果為1,而不是2。

圖片圖片

通過C++源碼我們可以看到,這是因?yàn)槿绻鸼lock引用的外部變量是值類型,會(huì)采取直接復(fù)制值的方式,而不是指針引用。

圖片圖片

想解決這個(gè)問題也很簡單,通過__block修飾一下值類型,即可實(shí)現(xiàn)block內(nèi)value的值和外部value參數(shù)統(tǒng)一。

圖片圖片

6.3靜態(tài)變量

我們看一下,如果捕獲的是一個(gè)static修飾的靜態(tài)變量,其結(jié)構(gòu)體會(huì)是什么實(shí)現(xiàn)。

圖片圖片

轉(zhuǎn)換為C++代碼后,可以看到原來的值傳遞變成了地址傳遞,__main_block_impl_0中value的引用是指針引用,在main函數(shù)中將value的地址傳入。如果被static修飾的本身就是一個(gè)對(duì)象,對(duì)象是通過指針引用的,在block的結(jié)構(gòu)體中就是兩個(gè)星號(hào)引用。也就是NSObject **obj。

圖片圖片

正是由于靜態(tài)變量地址傳遞的實(shí)現(xiàn),在block內(nèi)可以對(duì)靜態(tài)變量直接進(jìn)行更改,而無需用__block進(jìn)行修飾。

圖片圖片

6.4全局變量

如果把value改為全局變量,結(jié)構(gòu)體會(huì)有什么變化呢?

圖片圖片

因?yàn)槿肿兞康淖饔糜蚝艽螅圆⒉恍枰猙lock進(jìn)行單獨(dú)持有即可訪問,結(jié)構(gòu)體并不會(huì)新增字段。

圖片圖片

6.5對(duì)象類型變量

如果block中引用的是對(duì)象,而不是基礎(chǔ)數(shù)據(jù)類型,結(jié)構(gòu)體會(huì)是什么定義呢?

圖片圖片

執(zhí)行clang命令,執(zhí)行完成后結(jié)構(gòu)體是下圖的,下面代碼去掉了轉(zhuǎn)換,以及整理過代碼。可以看到多了兩個(gè)函數(shù)指針,__main_block_copy_0和__main_block_dispose_0。

以copy的實(shí)現(xiàn)__main_block_copy_0為例,執(zhí)行后會(huì)調(diào)用Block_object_assign的實(shí)現(xiàn),在實(shí)現(xiàn)中系統(tǒng)會(huì)根據(jù)person的引用方式,__strong、__weak、__unsafe_unretained,是強(qiáng)引用還是弱引用,調(diào)用對(duì)應(yīng)的內(nèi)存管理方法。

__main_block_dispose_0函數(shù)在block從堆區(qū)移除的時(shí)候被調(diào)用,調(diào)用dispose時(shí)會(huì)調(diào)用實(shí)現(xiàn)Block_object_dispose函數(shù),函數(shù)中會(huì)根據(jù)person的引用方式,進(jìn)行對(duì)應(yīng)的減少引用計(jì)數(shù)或釋放操作。

copy和dispose兩個(gè)函數(shù)都有一個(gè)3的參數(shù),這個(gè)參數(shù)是一個(gè)標(biāo)志位,表示外部變量類型。這里是BLOCK_FIELD_IS_OBJECT表示一個(gè)對(duì)象類型,也有BLOCK_FIELD_IS_WEAK表示weak引用的變量,BLOCK_FIELD_IS_BLOCK表示block類型的變量等。

圖片圖片


責(zé)任編輯:武曉燕 來源: 搜狐技術(shù)產(chǎn)品
相關(guān)推薦

2013-06-04 15:41:31

iOS開發(fā)移動(dòng)開發(fā)block

2009-06-15 15:57:21

Spring工作原理

2017-03-07 09:45:43

iOSBlock開發(fā)

2023-06-07 15:25:19

Kafka版本日志

2025-02-08 08:10:00

2013-07-19 12:52:50

iOS中BlockiOS開發(fā)學(xué)習(xí)

2023-02-22 07:04:05

自動(dòng)機(jī)原理優(yōu)化實(shí)踐

2024-02-27 22:31:00

Feign動(dòng)態(tài)代理核心

2011-08-08 18:11:45

IOS 4Block UIActionShe

2010-08-09 08:48:46

File APIWeb

2009-11-04 15:54:20

Portlet入門企業(yè)門戶

2010-02-26 17:54:54

python

2009-11-06 16:10:54

ClosureJavaScript開Google

2014-03-07 13:23:23

百度面試iOS

2009-08-25 13:48:01

Java EE架構(gòu)企業(yè)級(jí)應(yīng)用

2010-08-27 10:41:41

iPhone核心應(yīng)用程序

2013-07-19 14:00:13

iOS中BlockiOS開發(fā)學(xué)習(xí)

2013-07-19 14:35:59

iOS中BlockiOS開發(fā)學(xué)習(xí)

2023-12-07 08:07:47

Node流程代碼

2023-11-30 22:06:43

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产探花在线观看视频 | 国产一级黄色网 | 精品久久九 | 国产乱精品一区二区三区 | 亚洲狠狠爱一区二区三区 | 中文字幕一区在线观看视频 | 中文字幕一区二区三区不卡在线 | 一级黄色毛片a | 日日草夜夜草 | 日韩黄色小视频 | 国产美女网站 | 国产极品车模吞精高潮呻吟 | 国产成人精品一区二区三区在线 | 超碰人人91| 日本午夜视频 | 大陆一级毛片免费视频观看 | 国产精品九九 | 国产精品国产精品国产专区不卡 | 日本一区高清 | 欧美电影免费观看高清 | 久久久片| 久久精彩 | 国产二区精品视频 | 91丨九色丨国产在线 | 国产免费让你躁在线视频 | 精品欧美一区二区精品久久久 | 国产美女特级嫩嫩嫩bbb片 | 在线欧美小视频 | 久久久av| 欧美视频一区二区三区 | 中文字幕在线网 | 97精品久久| 免费超碰| 国产一区二区三区四区 | 日韩视频一区二区三区 | 一级一级毛片免费看 | 91精品国产一区二区三区 | 国产精品区二区三区日本 | 9191在线播放 | 日本在线一区二区三区 | 久久久91|