iOS 揭露Block的內部實現原理

2021-09-11 11:08:37 字數 2462 閱讀 7181

想必大家對block都很熟悉了,雖然都會用,但是你真的知道它的原理嗎?比如為什麼要加上__block,這個修飾符到底有什麼用?不加會有什麼後果?block又是如何實現的等等。。。該篇文章就為大家揭曉關於block的實現原理~

先給出問題,大家思考下結果吧,如果分別呼叫以下兩個方法,結果如何?

void blockfunc1()

; num = 200;

block();

}複製**

void blockfunc2()

; num = 200;

block();

}複製**

答案是

blockfunc1 : num equal 100

blockfunc2 : num equal 200

複製**

是不是有人答錯了?再來兩個函式。這兩個的結果與blockfunc2一樣,列印出來的 num 為 200

// 全域性變數

int num = 100;

void blockfunc3()

; num = 200;

block();

}複製**

void blockfunc4()

; num = 200;

block();

}複製**

疑問: 我們發現num做為區域性變數時加上 _ _block 修飾符、num做為全域性變數以及num為靜態區域性變數時在block中輸出結果是一樣的,皆為被修改之後的值,而做為區域性變數並且未加上__block的num在block中輸出的值卻還是未賦值之前的值。這是為什麼呢?探索這個問題我們就需要看看底層結構是如何實現的了

objective-c是乙個全動態語言,它的一切都是基於runtime實現的!在執行時會將oc轉換成c,我們可以利用這個來檢視關於block在內部是如何實現的 新建乙個command line tool專案,將以上**放入main.m中,如圖

這裡我們開啟終端,cd到專案目錄下,然後將用下面的命令將oc重寫為c

clang -rewrite-objc main.m

複製**

這時我們可以發現當前目錄下多了乙個main.cpp檔案,開啟它並滾到最下面

這裡我們可以看到blockfunc1的c語言實現方法

void

blockfunc1

()複製**

去掉型別轉換

void

blockfunc1

()複製**

這裡我們可以看到

block實際上是指向結構體的指標

該結構體為

我們來看下帶__block的blockfunc2

在 blockfunc1 中,block指向了乙個名為__blockfunc1_block_impl_0的結構體,並且在初始化時輸入了三個引數(__blockfunc1_block_impl_0最後的flags有預設引數,所以可以不用傳參),第三個引數就是我們寫的num,與blockfunc2相比較,這裡的num並沒有帶*號,所以說在這裡它只是傳值而非傳址,而下面的【num = 200;】也就沒什麼卵用了。這就是blockfunc2、blockfunc3與blockfunc4為什麼能列印出num改變後的值,而blockfunc1不行的原因。

在這裡我們也可以看出:

編譯器會將block的內部**生成對應的函式

** so **

我們總結下,block在內部會作為乙個指向結構體的指標,當呼叫block的時候其實就是根據block對應的指標找到相應的函式,進而進行呼叫,並傳入自身

我們再來看看 _ block, _block也被轉換成了結構體,並含有5個變數

struct __block_byref_num_0 ;

複製**

對應著blockfunc2中的

__block int num = 100;

複製**

當建立num並用__block修飾的時候,會初始化這五個變數 當我們執行

num = 200;

複製**

對應著

(num.__forwarding->num) = 200;

複製**

上面剛剛提到過 _ _forwarding是例項本身,即型別結構體__block_byref_num_0的&num,再找到對應的num變數,將其原來的100修改為200~~

iOS實現簡單的block的例子

oc中的block主要用來儲存一段 在需要的時候執行。下面是乙個小的例子 import viewcontroller.h inte ce viewcontroller myblock 定義乙個block property nonatomic,copy void myblock end impleme...

iOS 的 Block 的使用

block 是一種資料型別,用來存 用來儲存一段 想用 的時候就呼叫這段 使用場景 1.動畫 2.多執行緒 3.集合遍歷 4.網路請求 區別於函式 函式也是用來儲存一段 的,當呼叫函式的時候才會執行裡面的 但是函式不能在程式執行的時候再在指定的位置執行 1.沒引數也沒返回值的block void g...

block 塊的內部結構

每個oc物件都佔據著某個記憶體區域,因為例項變數的個數及物件所包含的關聯資料互不相同,所以每個物件所佔的記憶體區域大小也是有大有小,塊本身也是物件,在存放塊物件的記憶體區域中,首個變數是指向class物件的指標,該指標叫做isa。其餘記憶體裡含有塊丟向正常運轉所需的各種資訊。如下 塊 void is...