iOS Block __block說明符

系列文章:
iOS Block概念、語法及基本使用
iOS Block實現(xiàn)原理
iOS Block存儲域及循環(huán)引用

上一節(jié)我們已經明白Block的實質了,語法奇特蚂会,最終也是轉換為C語言來處理的园细。那么這一節(jié)來看看 __block修飾符到底干了啥,為什么使用了 __block修飾符昔馋,在Block內部就可以改變變量值了呢筹吐,帶著疑問來看下編譯后的源碼。

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block int val = 10;
        void (^blk)(void) = ^{
            val = 2;
            printf("%d\n",val);
        };
        blk();
    }
    return 0;
}

編譯后绒极,
一步一步來看:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

上述__block_impl結構體沒有變化骏令。

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
};

Desc結構體多了兩個函數(shù),copy dispose垄提,貌似從字面上可以猜測到是用來干嘛的榔袋,持有和銷毀周拐。

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*/);}

這兩個函數(shù)貌似不就是對應__main_block_desc_0結構體copy和dispose函數(shù)嘛,現(xiàn)在只是猜想凰兑,接著往下看妥粟。

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;
  }
};

來看看Block變量編譯后的Block結構體。多了 __Block_byref_val_0 *val結構體指針變量吏够,這個東西是什么呢勾给,帶有val,val不是我們的變量名稱嗎(int val = 10)锅知,構造函數(shù)參數(shù)也有相應增加播急。

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

臥槽看到了吧,原來把我們的變量轉成了結構體售睹。__block int val = 10;這是源代碼桩警,如果不加
__block說明符,val會變成__main_block_impl_0的一個變量(上節(jié)我們已經得知)昌妹,加上了__block說明符后捶枢,這個變量就變成了結構體了,神不神奇飞崖。

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref 上下文拿到__cself烂叔,也就是__main_block_impl_0結構體實例

            (val->__forwarding->val) = 2;
            printf("%d\n",(val->__forwarding->val));
        }

該函數(shù)賦值給結構體__block_impl內的FuncPtr函數(shù)指針。

int main(int argc, const char * argv[]) {
    /* @autoreleasepool*/ { __AtAutoreleasePool __autoreleasepool; 
    
        __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {//初始化
            (void*)0,
            (__Block_byref_val_0 *)&val,
            0,
            sizeof(__Block_byref_val_0),
            10
        };
        //block結構體實例初始化及賦值
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
        //block調用
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}

來看下main函數(shù)固歪,把__block修飾的變量初始化 為_Block_byref_val_0結構體變量蒜鸡,并把默認值10帶進去。Block的__main_block_impl_0結構體實例持有指向 __block 變量的__Block_byref_val_0結構體實例的指針昼牛。

__Block_byref_val_0結構體實例的成員變量_forwarding指針指向自己术瓮。通過__forwarding訪問成員變量val(成員變量val是該實例自己持有的變量,它相當于原自動變量贰健,也就是 int val= 10)胞四。

_forwarding指針.png

下面看看 single APP 使用__block說明符編譯后的源代碼:

源代碼
- (void)viewDidLoad {
    [super viewDidLoad];
    __block NSArray *arr = nil;
    void (^blk)(void) = ^{
        arr = [NSArray array];
    };
    blk();
}

編譯后:

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
  void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = {
    0,
    sizeof(struct __ViewController__viewDidLoad_block_impl_0),
    __ViewController__viewDidLoad_block_copy_0,
    __ViewController__viewDidLoad_block_dispose_0
};

//變量結構體
struct __Block_byref_arr_0 {
  void *__isa;
__Block_byref_arr_0 *__forwarding;
 int __flags;
 int __size;
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSArray *arr;
};

//賦值給__block_impl結構體實例內的FuncPtr函數(shù)指針 
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
  __Block_byref_arr_0 *arr = __cself->arr; // bound by ref

        (arr->__forwarding->arr) = ((NSArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("array"));
    }

//copy dispose
static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_assign((void*)&dst->arr, (void*)src->arr, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_dispose((void*)src->arr, 8/*BLOCK_FIELD_IS_BYREF*/);}

//block結構體實例
struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  __Block_byref_arr_0 *arr; // by ref
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, __Block_byref_arr_0 *_arr, int flags=0) : arr(_arr->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

//viewDidLoad函數(shù)(后邊為OC語言的默認參數(shù):id self, SEL _cmd)
static void _I_ViewController_viewDidLoad(ViewController * self, SEL _cmd) {
    ((void (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("ViewController"))}, sel_registerName("viewDidLoad"));
    
    __attribute__((__blocks__(byref))) __Block_byref_arr_0 arr = {
        (void*)0,
        (__Block_byref_arr_0 *)&arr,
        33554432,
        sizeof(__Block_byref_arr_0),
        __Block_byref_id_object_copy_131,
        __Block_byref_id_object_dispose_131,
        __null
    };
    
    //block結構體實例初始化并賦值
    void (*blk)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, (__Block_byref_arr_0 *)&arr, 570425344));
    
    //block調用
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}

以上就是__block說明符(具體說是存儲域類說明符)所做的事情。

當然肯定會有疑問的伶椿,copy dispose 函數(shù)指針只是提了一下辜伟,它們到底做了哪些事情沒有具體講,下節(jié)會講到脊另。

  • Block的種類及存儲域
  • __block變量存儲域
  • Block循環(huán)引用
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末导狡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子偎痛,更是在濱河造成了極大的恐慌旱捧,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異枚赡,居然都是意外死亡氓癌,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門贫橙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贪婉,“玉大人,你說我怎么就攤上這事卢肃∑S兀” “怎么了?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵莫湘,是天一觀的道長尤蒿。 經常有香客問我,道長逊脯,這世上最難降的妖魔是什么优质? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮军洼,結果婚禮上,老公的妹妹穿的比我還像新娘演怎。我一直安慰自己匕争,他們只是感情好,可當我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布爷耀。 她就那樣靜靜地躺著甘桑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪歹叮。 梳的紋絲不亂的頭發(fā)上跑杭,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天,我揣著相機與錄音咆耿,去河邊找鬼德谅。 笑死,一個胖子當著我的面吹牛萨螺,可吹牛的內容都是我干的窄做。 我是一名探鬼主播,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼慰技,長吁一口氣:“原來是場噩夢啊……” “哼椭盏!你這毒婦竟也來了?” 一聲冷哼從身側響起吻商,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤掏颊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后艾帐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乌叶,經...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡盆偿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了枉昏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陈肛。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖兄裂,靈堂內的尸體忽然破棺而出句旱,到底是詐尸還是另有隱情,我是刑警寧澤晰奖,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布谈撒,位于F島的核電站,受9級特大地震影響匾南,放射性物質發(fā)生泄漏啃匿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一蛆楞、第九天 我趴在偏房一處隱蔽的房頂上張望溯乒。 院中可真熱鬧,春花似錦豹爹、人聲如沸裆悄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽光稼。三九已至,卻和暖如春孩等,著一層夾襖步出監(jiān)牢的瞬間艾君,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工肄方, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留冰垄,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓扒秸,卻偏偏與公主長得像播演,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子伴奥,可洞房花燭夜當晚...
    茶點故事閱讀 44,960評論 2 355