天天看點

OC中block的底層實作和具體運用(轉)

原文位址:點選打開連結

這段時間一直忙着開發,好久沒來部落格了,人氣稀少了不少,準備持續更新一個專題,主要談談OC底層的一些實作機制,并會對一些經典的面試題進行分析,感興趣的可以持續關注,第一講的主題是: BLock的實作和運用

   我們一起來看看,經Clang編譯後的block結構如下:

OC中block的底層實作和具體運用(轉)
1 struct Block_literal_1 {
 2     void *isa; 
 3     int flags;
 4     int reserved;
 5     void (*invoke)(void *, ...);
 6     struct Block_descriptor_1 {
 7     unsigned long int reserved;       
 8         unsigned long int size;      
 9         // optional helper functions
10         void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
11         void (*dispose_helper)(void *src);             // IFF (1<<25)
12         // required ABI.2014.5.25
13         const char *signature;                         // IFF (1<<30)
14     } *descriptor;
15     // imported variables
16 };      
OC中block的底層實作和具體運用(轉)

         可以看到在Block結構體中含有isa指針,這就證明了Block其實就是對象,并具有一般對象的所有功能。這個isa指針被初始化為

_NSConcreteStackBlock

或者

_NSConcreteGlobalBlock

類的位址。在沒有開啟ARC的情況下,如果Block中包含有局部變量則isa被初始化為前者,否則就被初始化為後者。而當ARC開啟後,如果Block中包含有局部變量則isa被初始化為

_NSConcreteMallocBlock

,否則就被初始化為

_NSConcreteGlobalBlock

。invoke是一個函數指針,它指向的是Block被轉換成函數的位址。最後的imported variables部分是Block需要通路的外部的局部變量,他們在編譯就會被拷貝到Block中,這樣一來Block就是成為一個閉包了。

在iOS開發中我們在很多地方都能見到block的身影,如:       (1)周遊數組或者字典       (2)視圖動畫       (3)排序       (4)通知       (5)錯誤處理       (6)多線程       (7)封裝變化點 .......      是以,我們了解到Block是OC中的一種資料類型,在iOS開發中被廣泛使用,^是Block的特有标記,Block的實作代碼包含在{}之間.大多情況下,以内聯inline函數的方式被定義和使用,Block與C語言的函數指針有些相似,但使用起來更加靈活, 一個簡單的加法,使得block的定義一目了然:

OC中block的底層實作和具體運用(轉)
1 int main(int argc, const char * argv[])
 2 {
 3 
 4     @autoreleasepool {
 5         int (^sum)(int, int) = ^(int a, int b){
 6             return  a + b;
 7         };
 8         int add = sum(4, 5);
 9         NSLog(@"%d", add);
10     }
11     return 0;
12 }      
OC中block的底層實作和具體運用(轉)

  (一) Block可以使用在定義之前聲明的局部變量      (1)在定義Block時,會在Block中建立目前局部變量内容的副本(拷貝)      (2)後續再對該變量的數值進行修改,不會影響Block中的數值      (3)如果需要在block中保持局部變量的數值變化,需要使用__block關鍵字      (4)使用__block關鍵字後,同樣可以在Block中修改該變量的數值

OC中block的底層實作和具體運用(轉)
1 int main(int argc, const char * argv[])
 2 {
 3 
 4     @autoreleasepool {
 5         int i = 0;
 6         void (^myBlock)() = ^{
 7             NSLog(@"%d",i);
 8         };
 9         i = 100;
10         myBlock();
11     }
12     return 0;
13 }
14 
15 運作結果: i = 0
16 
17 int main(int argc, const char * argv[])
18 {
19 
20     @autoreleasepool {
21        __block int i = 0;
22         void (^myBlock)() = ^{
23             NSLog(@"%d",i);
24         };
25         i = 100;
26         myBlock();
27     }
28     return 0;
29 }
30 
31 
32 運作結果: i=100      
OC中block的底層實作和具體運用(轉)

(二) clang編譯block封裝的語句,一窺其廬山真面目

^{printf("OC is Good");}      

編譯後:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

struct

__block_literal_1 {

void

*isa;

int

flags;

int

reserved;

void

(*invoke)(

struct

__block_literal_1 *);

struct

__block_descriptor_1 *descriptor;

};

void

__block_invoke_1(

struct

__block_literal_1 *_block) {

printf(

"OC is Good"

);

}

static

struct

__block_descriptor_1 {

unsigned 

long

int

reserved;

unsigned 

long

int

Block_size;

} __block_descriptor_1 = { 0, 

sizeof

(

struct

__block_literal_1), __block_invoke_1;}

 我們可以觀察到:通過iSa指針,内容在編譯的時候就會被拷貝到block中,進而形成閉包.  先體會下底層實作,接下來将會重點介紹block在OC的中的常見應用場景,今天的知識點,一句話概括:閉包就是能夠讀取其它函數内部變量的函數.