Objective-C:探索block(一)

本篇文章主要講block的基本使用和底層實現,以下將block的講解分成三小節:

一. 什么是block

block 表面語義指“塊”,在Objective-C中block就是能執行某些任務的代碼塊,是對C語言的擴充,從它的語法上看(請看 block的語法),block相當于帶有局部變量的匿名函數。在Objective-C編程中使用block可以簡化代碼結構,使代碼的業務邏輯更清晰。

二. block語法

1.block代碼體定義

^返回類型(參數0,參數1,……){……} 其中返回類型,參數列表可以省略

示例代碼:

帶參有返回值:^void(int i){ NSLog(@"%d",i);}
無參無返回值:^{NSLog(@"I'am block");}

2.block 類型變量

從上面的block代碼體的定義中,知道了怎樣寫一個代碼塊。但問題是如何來引用該代碼塊? 拋磚引玉,又如我們怎樣引用字符串@"hello world",一般會先定義字符串類型變量再把字符串賦值給該變量:NSString *str=@"hello world",之后就可以通過str變量來引用@"hello world"。同理,要引用代碼塊,我們可以先定義一個block 類型變量,block類型變量定義:

返回類型 (^變量名)(參數0,參數1,...)

示例:

void (^myprint)(int i)=^void(int i){NSLog(@"%d",i);};
void (^myprint)(int i)=^(int i){NSLog(@"%d",i);};
void (^myprint)(void)=^{NSLog(@"hello world");};

注意:block類型變量定義,即使它的返回類型為void和參數列表為空都不能省略

3.用typedef 定義block 數據類型

如果block類型變量在代碼中多處出現或作為函數的參數和返回值時,它的書寫將變得很繁雜。通常,我們會用宏定義typedef 來重新定義block數據類型

typedef 返回類型(^代碼塊類型名)(參數列表);

示例:

typedef void(^Myprint)(int i);
#import <Foundation/Foundation.h>
int main(int argc,const char *argv[]){
  @autoreleasepool{
    Myprint myprint=^void(int i){NSLog(@"%d",i);};
    myprint(1);//使用block
  }
    return 0;
}

三. block的底層實現

想窺探block的底層實現,我們要把OC反編譯成C++語言,看看block整體定義包含了哪些內容。貼出要做反編譯的OC代碼文件 main.m

#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^myprint)(void) = ^{
            NSLog(@"Hello World!");
        };
        
        myprint();
    }
    return 0;
}

進入到工程目錄,在終端執行編譯指令:clang -rewrite-objc main.m

這里寫圖片描述

編譯后可以在main.cpp文件的尾部可以看到block 的整體定義有四部分:

//1.此結構體記錄block的描述信息,它在定義時順便初始化了個實例__main_block_desc_0_DATA
static struct __main_block_desc_0 {
//size_t可以理解成 unsigned long(但不嚴謹)
  size_t reserved; //指明block在內存中要保留一塊內存空間的大小,這塊內存區暫沒用途。
  size_t Block_size;//指明block的大小 == sizeof(struct __main_block_impl_0) 
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

//2.存儲與block實現相關的信息
static struct __block_impl {
  void *isa;//isa是一個void *類型(空指針類型可以指向任何類型,相當于id),在這里isa是指向了block實例,也就是指向了自己在內存中的起始地址
  int Flags; //系統默認值為0
  int Reserved; //構造方法里沒看到賦值,應該是用來存儲block保留內存空間大小
  void *FuncPtr; //指向block代碼體實現的函數指針,block的調用關鍵就它了
}

//3.
//__main_block_impl_0就是block(myprint)的定義,它就是一個結構體里面包含了另外兩個struct和一個結構體的構造方法(用來初始化一個結構體實例)
struct __main_block_impl_0 {
  struct __block_impl impl; //存儲與block實現(代碼體)相關的信息,請看//2.
  struct __main_block_desc_0* Desc; //存儲block的描述信息
  //構造方法,這個方法是重點
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int     flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//4.block代碼體的實現函數,就是一個C函數,可以通過函數指針來調用
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
            printf("Hello World!");
        }
        
//這里是主函數,調用了block
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
     
//下面做了各種轉換,拆解一下
//1.void (*myprint)(void):聲明一個返回類型為空,參數為空,名稱叫myprint的函數指針,它指向了block的構造方法,實質它是指向block結構體實例的指針
//2.(void (*)(__block_impl *))這是一個返回值為void,參數類型為__block_impl *的指針類型,用來修飾FuncPtr
        void (*myprint)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));                    
        ((void (*)(__block_impl *)) ((__block_impl *)myprint)->FuncPtr)((__block_impl *)myprint);
    }
    return 0;
}
//轉換簡化一下如下:
//struct __main_block_impl_0 tmp = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
//struct __main_block_impl_0 *myprint = &tmp;
//(*myprint->impl.FuncPtr)(myprint);

由block的底層定義可以知道,block類型變量其實它是一個結構體實例與Objective-C對象極為相似,它的底層定義也解釋了block為什么既可以像變量一樣定義和作為參數被傳遞,也可以像函數一樣被調用。

Block還有其它重要知識點:

  1. 基本類型變量與對象的截取
  2. __block修飾符
  3. block存儲域
  4. copy的使用
  5. 相互引用問題
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容