ios學習 block深度解析

2021-07-26 22:03:19 字數 4490 閱讀 4350

1. block的本質是乙個objective-c的物件,為什麼這麼說?

在objective-c中,runtime會在執行時根據物件的isa指標的指向,來度額定這個物件的型別,也可以認為乙個物件,它具有isa指標,就是乙個oc物件

2. 你怎麼知道block有isa指標呢,我們可以通過clang命令將來看block的實現

//測試**

int main(int argc, const

char * argv) ;

}return

0;}

轉化後:block物件被編譯器轉化成了乙個__main_block_impl_0 

的結構體如下

struct

__main_block_impl_0

};

這個結構體由三部分組成impl的結構體+desc的結構體指標+建構函式,建構函式是用來對結構體做初始化的,剩下的那兩個結構體對應的實現如下

//impl的型別,block的相關實現資訊

struct __block_impl ;

//對於block的描述資訊

struct __main_block_desc_0 __main_block_desc_0_data = ;

block會被編譯器轉化為__main_block_impl_0的結構體,這個結構體由三部分組成:儲存block的相關實現資訊的_block_impl的結構體impl以及儲存block的描述資訊的__main_block_desc_0的結構體指標desc以及用來對block做初始化的構造方法,其中impl中的isa的成員說明了block是乙個oc物件,還有乙個乙個函式指標指向了block的實現

3. block:其實相當於c語言中的匿名函式,他可以訪問外部變數或者物件

1.沒有函式名,最純潔的block^;,最純潔的函式最起碼要有個函式名 

2. 帶有脫字元』^』 

3.不管c語言還是oc函式都不支援函式巢狀,但是block實現了這個功能,它相當於乙個函式,但是他的視線卻是在另外乙個函式的內部。

#
自動變數:就是區域性變數。訪問區域性變數相當於函式呼叫時的值傳遞,不允許修改:當訪問了外部的變數,他會把他訪問到的自動變數作為結構體成員新增到他的結構體當中,而且和原變數同名同值,不能修改的原因是,他們除了值一樣沒有練習,上面說到,block相當於乙個函式,兩個函式裡面有仙童的區域性變數,乙個變了並不會對另外乙個產生影響,所以編譯器乾脆就禁止了,實現如下

//block訪問外部自動變數

struct

__main_block_impl_0

};

#
2 . 靜態變數,在其作用範圍內只有一分記憶體,對其可讀可寫。相當於函式呼叫時的位址傳遞,block會將它訪問到的外部靜態變數的位址新增到自己的結構體成員中,有了位址,改值又有什麼不可以呢,實現如下

//截獲靜態變數值

struct

__main_block_impl_0

};

#
3 . 全域性變數:在全域性可以訪問到的,在全域性符號表中,與在block外訪問沒有區別,可讀可寫 

對於靜態、全域性變數的區別,可以戳這裡我覺得可以點

#
4 .物件,block可以讀物件,不能修改物件指標的指向。

#
5 .物件的屬性:可讀可寫

4. 如何讓block可以修改截獲的自動變數的值

4. 修改block所截獲的外部變數的值

上面說到:block不可以修改自動變數或者物件的指向,這個時候,__block就像乙個救世主一樣出現在了我們面前,__block是如何實現修改自動變數值的,看一下他的實現

struct

__main_block_impl_0

};//被block修飾的自動變數被這樣的乙個結構體指標儲存了其位址

struct

__block_byref_a_0 ;

static void __main_block_func_0(struct

__main_block_impl_0 *__cself)

從上面可以看出,當用__block修飾自動變數的時候,這個變數變成了乙個struct __block_byref_a_0的結構體例項,這個結構體有乙個指向自身的指標,forwarding,來保證這個變數不管在什麼位置都能被正確訪問到,(後面會詳細解釋) 

修改變數的過程:impl會根據自己儲存的指向struct __block_byref_a_0的結構體例項的指標,根據這個指標找到farwarding指標找到自己,在找到裡面儲存的自動變數,修改他的值。

5. block的儲存域

在前面,我們發現block本質是乙個物件,他有乙個isa指標,指向其所對應的類,那麼他所對應的類是什麼呢,這就牽扯到block的儲存域了

block的儲存於域有三種型別,其中isa指標指向這三種中的其中一種 

1._nsconcretestackblock:儲存在棧上 

2._nsconcreteglobalblock:儲存在資料區 

3._nsconcretmallocblock:儲存在堆上

對於這三種型別的使用情況

_nsconcreteglobalblock: 

1. 凡是所有的全域性block都儲存在資料區 

2. 如果block中沒有截獲自動變數,block也在資料區

_nsconcretestackblock: 

1. mrc:預設在棧區,block生命週期與其作用域相關 

2.arc:大多數arc情況下的block是儲存在堆區的,只有少部分在棧區。在棧區需要我們手動 copy 到堆區 

_nsconcretmallocblock 

mrc:通過copy的方法可以將block從棧區複製到堆區 

arc:大多數預設情況在堆區,有棧區的可以通過copy/strong複製到堆區,只要有乙個strong指向他,就會被複製到堆區

6. 通過copy將block從棧上覆制到堆上

在棧上的變數的生命週期由編譯器來管理,與其作用域相關聯,出了作用域就會被釋放,但是堆上有程式設計師自己管理或者arc管理,不會因為其出了作用域就被釋放

前面提到了乙個forwarding指標,用block修飾的自動變數會被儲存在乙個__block_byref_a_0的結構體指標中,這個指標中又有乙個指向自己的forwarding,那麼為什麼不直接找到其對應的值,而要通過這個指標呢,因為當block物件被從棧上賦值到堆上的時候,其內部用到的被block修飾的變數也會被賦值乙份放在堆上,然後讓棧上forwarding結構體指標指向堆上的結構體就可以,這樣就可以保證不管在棧上還是堆上,都可以正確訪問到變數

arc 下需要不需要手動copy 的情況

當 block 被強引用時

系統的 api 中帶有 usingblock 時

block 作為函式返回值

那什麼時候需要我們手動 copy 呢?

當block 作為函式引數的時候,在 arc 下我們自定義的 block 要寫上 copy。

關於copy的特點

如果原來在棧上,通過copy,被複製到堆上。

如果原來在全域性資料區,不會發生改變

如果在堆區:其引用計數加1

7. __block在arc和mrc下的區別

1、 用__block修飾,不管在mrc還是arc下,都可以修改自動變數的值 

2、在mrc下會避免迴圈引用,因為mrc下用__block修飾的變數不會被reatin,所以不會被 block 持有,所以可以避免迴圈引用 

3、 在arc下解決迴圈引用用的是weak:arc下的迴圈引用並不是有__block引起的,是因為arc的特性,預設是強引用,所以為了解決迴圈引用,self一般用weak來修飾

8. 為什麼要用copy來修飾block

使用copy可以將block從棧上轉移到堆上 

mrc下,預設是棧上為了控制block生命週期,需要將其copy的堆上,不可以用reatin代替。 arc下大多數情況預設是在堆上,但是因為一般遵循傳統,會寫上copy,但是可以用strong來代替。

ios 全面解析block

block block變數格式 返回值型別 不可省略,最少void,沒有 變數名稱 引數 不可省略,至少 無引數無返回值 void noparamblock noparamblock void voidparamblock void void voidparamblock void blocknam...

ios 全面解析block

typedef int myblock void cfun void blockname1 myblock blockname2 void ocfun void blockname1 andotherblock myblock blockname2 在oc中呼叫cfun,直接cfun就可以了,但是o...

iOS開發之block解析

1.block的本質是乙個objective c的物件。為什麼這麼說?在objective c中。runtime會在執行時依據物件的isa指標的指向,來度額定這個物件的型別。也能夠覺得乙個物件。它具有isa指標。就是乙個oc物件 2.你怎麼知道block有isa指標呢。我們能夠通過clang命令將來...