關(guān)于Block的幾點(diǎn)思考

背景

block的問(wèn)題算是老生常談了,本以為對(duì)block很熟悉了,但是前幾天跟同事聊到幾個(gè)block的很有意思的點(diǎn),一開(kāi)始還真的感覺(jué)一臉懵逼。想了很久才想出來(lái)自以為能解釋的原因,大家也可以一起探討

block內(nèi)嵌block的變量捕獲

- (void)embeddedBlock
{
    void (^blockA)() = ^{
        void (^blockB)() = ^{
            NSLog(@"%@",self);
        };
    };
}

上面是一段很有意思的代碼,blockB會(huì)捕獲self,這是大家都了解的,但是blockA會(huì)捕獲self么?為什么?

一開(kāi)始聽(tīng)到這個(gè)問(wèn)題也是一臉懵逼,到底外面的block會(huì)不會(huì)捕獲self呢?

我們來(lái)看源碼:

struct __ClassA__embeddedBlock_block_impl_0 {
  struct __block_impl impl;
  struct __ClassA__embeddedBlock_block_desc_0* Desc;
  // ③ 定義成員變量self
  ClassA *self;
  __ClassA__embeddedBlock_block_impl_0(void *fp, struct __ClassA__embeddedBlock_block_desc_0 *desc, ClassA *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ClassA__embeddedBlock_block_func_0(struct __ClassA__embeddedBlock_block_impl_0 *__cself) {
  ClassA *self = __cself->self; // bound by copy

            NSLog((NSString *)&__NSConstantStringImpl__var_folders_pv_88jpx_sn429_1nx8xzc8g7w00000gn_T_ClassA_64c0bc_mi_0,self);
        }
static void __ClassA__embeddedBlock_block_copy_0(struct __ClassA__embeddedBlock_block_impl_0*dst, struct __ClassA__embeddedBlock_block_impl_0*src) {_Block_object_assign((void*)&dst->self, (void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __ClassA__embeddedBlock_block_dispose_0(struct __ClassA__embeddedBlock_block_impl_0*src) {_Block_object_dispose((void*)src->self, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __ClassA__embeddedBlock_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __ClassA__embeddedBlock_block_impl_0*, struct __ClassA__embeddedBlock_block_impl_0*);
  void (*dispose)(struct __ClassA__embeddedBlock_block_impl_0*);
} __ClassA__embeddedBlock_block_desc_0_DATA = { 0, sizeof(struct __ClassA__embeddedBlock_block_impl_0), __ClassA__embeddedBlock_block_copy_0, __ClassA__embeddedBlock_block_dispose_0};

struct __ClassA__embeddedBlock_block_impl_1 {
  struct __block_impl impl;
  struct __ClassA__embeddedBlock_block_desc_1* Desc;
  // ① 定義成員變量self
  ClassA *self; 
  __ClassA__embeddedBlock_block_impl_1(void *fp, struct __ClassA__embeddedBlock_block_desc_1 *desc, ClassA *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __ClassA__embeddedBlock_block_func_1(struct __ClassA__embeddedBlock_block_impl_1 *__cself) {
  ClassA *self = __cself->self; // bound by copy
    // 將self從blockA中傳入到blockB中,blockB捕獲self
        void (*blockB)() = ((void (*)())&__ClassA__embeddedBlock_block_impl_0((void *)__ClassA__embeddedBlock_block_func_0, &__ClassA__embeddedBlock_block_desc_0_DATA, self, 570425344));
    }

static struct __ClassA__embeddedBlock_block_desc_1 {
  size_t reserved;
  size_t Block_size;
} __ClassA__embeddedBlock_block_desc_1_DATA = { 0, sizeof(struct __ClassA__embeddedBlock_block_impl_1)};

static void _I_ClassA_embeddedBlock(ClassA * self, SEL _cmd) {
// ②將self從外界傳入到blockA中,blockA捕獲self
    void (*blockA)() = ((void (*)())&__ClassA__embeddedBlock_block_impl_1((void *)__ClassA__embeddedBlock_block_func_1, &__ClassA__embeddedBlock_block_desc_1_DATA, self, 570425344));
}

可以看到,①和③處,blockA,blockB都定義了成員變量self。②,③處變量的傳遞,先將self從外界傳入到blockA中。再?gòu)腷lockA的中傳入到blockB中。

blockB只能從blockA的作用域里捕獲變量。因此blockB中捕獲的任何東西,blockA必須也捕獲一份。

成員變量的捕獲

- (void)catchIvar
{
    void (^blockA)() = ^{
        NSLog(@"%@",_ivarA);
    };
}

我們知道,上面的代碼實(shí)際會(huì)捕獲self。他跟下面的代碼等價(jià)

- (void)catchIvar
{
    void (^blockA)() = ^{
        NSLog(@"%@",self->_ivarA);
    };
}

但是系統(tǒng)為什么要轉(zhuǎn)化成self->_ivarA的形式去捕獲self呢?而不是直接捕獲_ivarA呢?他這么設(shè)計(jì)的目的在哪?

這個(gè)問(wèn)題我想了很久,最后想到了一個(gè)可以自圓其說(shuō)的解釋,也歡迎大家一起交流討論

正常的方法中,直接使用成員變量ivar,編譯器都會(huì)自動(dòng)轉(zhuǎn)為self->ivar。之所以這樣做,是因?yàn)檎5南l(fā)生中,self是會(huì)作為一個(gè)參數(shù),傳遞到所有方法中的。所以在任何方法方法中都可以直接獲取到self。而成員變量ivar,并沒(méi)有作為任何參數(shù)傳入到方法中。所以為了訪問(wèn)到ivar,正常都會(huì)轉(zhuǎn)化為self->ivar來(lái)訪問(wèn)。

這個(gè)轉(zhuǎn)化從iOS很早的版本就有了。而block是iOS4才開(kāi)始出現(xiàn)。所以block里面的ivar轉(zhuǎn)化為self->ivar,可能是為了跟其他地方保持一致性吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容