Block深層剖析(三)

第一篇文章 Block深層剖析(一)介紹了Block的一些基礎概念和用法。
第二篇文章 Block深層剖析(二)分析了最簡單的Block源碼。
這篇文章 將會介紹有關Block截獲的相關特點和源碼分析。
首先 總結一下Block截獲的特點:

(1)對于基本數據類型局部變量 截獲其值。
(2)對于對象類型的局部變量 連同其所有權修飾符一起截獲。
(3)以指針形式截獲局部靜態變量。
(4)不截獲全局變量和靜態全局變量。

1.截獲局部變量值

1.1查看源碼

#include "stdio.h"
int global_var = 110;
static int static_global_var = 119;
int main() {
    int a = 0;
    int val = 123;
    static int static_var = 120;
    __strong id strong_object = NULL;
    void(^blockName)(void) = ^{
        printf("局部 基本數據類型 變量 val = %d\n",val);
        printf("局部 靜態變量 static_var = %d\n",static_var);
        printf("全局變量 global_var = %d\n",global_var);
        printf("全局靜態變量 static_global_var = %d\n",static_global_var);

        printf("局部__strong對象類型變量 strong_object = %p\n",strong_object);

    };
    blockName();
    return 0;
}

clang -rewrite-objc a.c 后得到的block源碼,省略與上一篇文章中分析過的源碼相同的 __block_impl結構體和__main_block_desc_0結構體。

int global_var = 110;
static int static_global_var = 119;

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int val;
  int *static_var;
  __strong  id strong_object;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, 
int _val, int *_static_var, id _strong_object, int flags=0)
 : val(_val), static_var(_static_var), strong_object(_strong_object) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// copy函數的調用時機:棧上的Block復制到堆上時
// 只有調用了copy函數才能持有截獲的附有__strong修飾符的對象類型的自動變量值,如果不調用即使截獲了對象,它也會隨著變量的作用域結束而被廢棄。
因此在Block中使用對象類型的自動變量時,需要調用Block的copy函數
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src)
{
_Block_object_assign(
(void*)&dst->strong_obj, (void*)src->strong_obj, 3
/*BLOCK_FIELD_IS_OBJECT*/);
}
// dispose函數調用時機:堆上的Block被廢棄時
static void __main_block_dispose_0(struct __main_block_impl_0*src)
{
_Block_object_dispose((void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}


static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int val = __cself->val;
  int *static_var = __cself->static_var; 
  id strong_object = __cself->strong_object; 

        printf("局部 基本數據類型 變量 val = %d\n",val);
        printf("局部 靜態變量 static_var = %d\n",(*static_var));
        printf("全局變量 global_var = %d\n",global_var);
        printf("全局靜態變量 static_global_var = %d\n",static_global_var);

        printf("局部__strong對象類型變量 strong_object = %p\n",strong_object);

    }
int main() {
    int a = 0;
    int val = 123;
    static int static_var = 120;
    __attribute__((objc_ownership(strong))) id strong_object = __null;
    void(*blockName)(void) = ((void (*)())&__main_block_impl_0(
(void *)__main_block_func_0, &__main_block_desc_0_DATA, 
val, &static_var, strong_object, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)blockName)->FuncPtr)((__block_impl *)blockName);
    return 0;
}

1.2源碼分析

對比與之前的不同之處發現:

(1)Block語法表達式中使用的局部變量val ,作為成員變量添加到了__main_block_impl_0結構體中。

(2)局部靜態變量static_var,以指針類型的成員變量添加到__main_block_impl_0結構體中。

(3)對象類型的局部變量strong_obj,連同其所有權修飾符一起截獲作為成員變量添加到__main_block_impl_0結構體中。

copy函數的調用時機:棧上的Block復制到堆上時
只有調用了copy函數才能持有截獲的附有__strong修飾符的對象類型的自動變量值,如果不調用即使截獲了對象,它也會隨著變量的作用域結束而被廢棄。
因此在Block中使用對象類型的自動變量時,需要調用Block的copy函數。

dispose函數調用時機:堆上的Block被廢棄時。

(4)全局變量global_var和全局靜態變量static_global_var沒有作為成員變量添加到__main_block_impl_0結構體中。

這里注意在Block語法表達式中沒有使用的局部變量a,是不會添加到__main_block_impl_0結構體中的

總結:“截獲自動變量值” 意味著在執行Block語法時,Block語法表達式中使用的自動變量被保存到Block的結構體實例中。

2.__block說明符

__block修飾符是為了解決在Block語法中不能修改截獲變量問題(注意"使用"和"賦值"是不一樣的)。__block說明符全稱:"__block存儲域說明符"。

存儲域說明符用于指定將變量值設置到哪個存儲域中。
如auto表示作為自動變量存儲在棧中;
static表示作為靜態變量存儲在數據區中

下面結合源碼進行分析

2.1查看源碼

#include "stdio.h"
int main()
{
   __block int val = 123;
   void(^blockName)(void) = ^{
       val = 456;
       printf("局部變量 val = %d\n",val);
   };
   blockName();
   return 0;
}

clang -rewrite-objc a.c 后得到的block源碼。

struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_val_0 *val; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, 
__Block_byref_val_0 *_val, int flags=0) 
: val(_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref
       (val->__forwarding->val) = 456;
       printf("局部變量 val = %d\n",(val->__forwarding->val));
   }
static void __main_block_copy_0(struct __main_block_impl_0*dst
, struct __main_block_impl_0*src) {
_Block_object_assign((void*)&dst->val, (void*)src->val, 8
/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {
_Block_object_dispose((void*)src->val, 8
/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 
0, sizeof(struct __main_block_impl_0)
, __main_block_copy_0, __main_block_dispose_0};

int main()
{
   __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {
(void*)0,(__Block_byref_val_0 *)&val
, 0, sizeof(__Block_byref_val_0), 123};

   void(*blockName)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
&__main_block_desc_0_DATA, 
(__Block_byref_val_0 *)&val, 570425344));

   ((void (*)(__block_impl *))
((__block_impl *)blockName)->FuncPtr)((__block_impl *)blockName);

   return 0;
}

2.2源碼分析

2.2.1 __block變量val

__block int val = 123;

clang后源碼

 __Block_byref_val_0 val = {
(void*)0,(__Block_byref_val_0 *)&val
, 0, sizeof(__Block_byref_val_0), 123};

我們發現__block變量val 變成了__Block_byref_val_0結構體類型的自動變量。

struct __Block_byref_val_0 {
  void *__isa;
__Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};

查看__Block_byref_val_0結構體聲明,發現val變量出現在改結構體中,這就意味著
__Block_byref_val_0結構體持有相當于原自動變量的成員變量。

2.2.2 __block變量賦值

^{
       val = 456;
       printf("局部變量 val = %d\n",val);
   };

clang后源碼

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref

       (val->__forwarding->val) = 456;
       printf("局部變量 val = %d\n",(val->__forwarding->val));
   }
  • Block的__main_block_impl_0結構體實例 持有指向__block變量的__Block_byref_val_0結構體實例的指針。
  • __Block_byref_val_0結構體實例的成員變量__forwarding 持有指向該實例自身的指針。
    通過成員變量__forwarding可以訪問到成員變量val,上面說過這個成員變量val相當于原自動變量。

2.2.3 __block變量的捕獲

Block的__main_block_impl_0結構體實例 持有指向__block變量的__Block_byref_val_0結構體實例的指針。

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