Block解析

什么是Block

  • Block是將函數(shù)及其執(zhí)行上下文封裝起來的對(duì)象异剥。

    #import "MCBlock.h"
    
    @implementation MCBlock
    
    - (void)method
    {
        static int multiplier = 6;
        int(^Block)(int) = ^int(int num)
        {
            return num * multiplier;
        };
        
        Block(2);
    }
    
    @end
    

    經(jīng)過命令行重寫clang -rewrite-objc MCBlock.m,生成文件MCBlock.cpp

    //I代表實(shí)例方法
    static void _I_MCBlock_method(MCBlock * self, SEL _cmd) {
        int multiplier = 6;
        int(*Block)(int) = ((int (*)(int))&__MCBlock__method_block_impl_0((void *)__MCBlock__method_block_func_0, &__MCBlock__method_block_desc_0_DATA, multiplier));
    
        ((int (*)(__block_impl *, int))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block, 2);
    }
    
    struct __MCBlock__method_block_impl_0 {
      struct __block_impl impl;
      struct __MCBlock__method_block_desc_0* Desc;
      int multiplier;
      __MCBlock__method_block_impl_0(void *fp, struct __MCBlock__method_block_desc_0 *desc, int _multiplier, int flags=0) : multiplier(_multiplier) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    static int __MCBlock__method_block_func_0(struct __MCBlock__method_block_impl_0 *__cself, int num) {
      int multiplier = __cself->multiplier; // bound by copy
    
            return num * multiplier;
        }
    
    struct __block_impl {
      void *isa;//isa指針,Block是對(duì)象的標(biāo)志
      int Flags;
      int Reserved;
      void *FuncPtr;//函數(shù)指針
    };
    
  • Block的調(diào)用即是函數(shù)的調(diào)用

Block截獲變量

//滴滴面試
{
    static int multiplier = 6;
    int(^Block)(int) = ^int(int num)
    {
        return num * multiplier;
    };
    multiplier = 4;
    NSLog(@"result is %d",Block(2));//result is 12
}

  • 局部變量
    • 基本數(shù)據(jù)類型
    • 對(duì)象類型
  • 靜態(tài)局部變量
  • 全局變量
  • 靜態(tài)全局變量

關(guān)于Block的截獲特性你是否有了解慎璧,以及Block的截獲特性又是怎樣的?

變量的截獲方式

  • 對(duì)于基本數(shù)據(jù)類型的局部變量截獲其值挺身。
  • 對(duì)于對(duì)象類型的局部變量連同所有權(quán)修飾符一起截獲摔笤。
  • 指針形式截獲局部靜態(tài)變量。
  • 不截獲全局變量个粱、靜態(tài)全局變量。
#import "MCBlock.h"

@implementation MCBlock
//全局變量
int global_var = 4;
//靜態(tài)全局變量
static int static_global_var = 5;

- (void)method
{
    //基本數(shù)據(jù)類型的局部變量
    int var = 1;
    
    //對(duì)象類型的局部變量
    __unsafe_unretained id unsafe_obj = nil;
    __strong id strong_obj = nil;
    
    static int static_var = 3;
    
    void(^Block)(void) = ^{
        NSLog(@"局部變量<基本數(shù)據(jù)類型> var %d", var);
        NSLog(@"局部變量<__unsafe_unretained 對(duì)象類型> var %@", unsafe_obj);
        NSLog(@"局部變量<__strong 對(duì)象類型> var %@", strong_obj);
        
        NSLog(@"靜態(tài)變量 %d", static_var);
        NSLog(@"全局變量 %d", global_var);
        NSLog(@"靜態(tài)全局變量 %d", static_global_var);
    };
    
    Block();
}

@end

clang -rewrite-objc -fobjc-arc MCBlock.m

// @implementation MCBlock

int global_var = 4;

static int static_global_var = 5;


struct __MCBlock__method_block_impl_0 {
  struct __block_impl impl;
  struct __MCBlock__method_block_desc_0* Desc;
//截獲局部變量的值
  int var;
  //連同所有權(quán)修飾符一起截獲
  __unsafe_unretained id unsafe_obj;
  __strong id strong_obj;
  //以指針形式截獲局部變量
  int *static_var;
  //對(duì)全局變量翻翩、靜態(tài)全局變量不截獲
  
  __MCBlock__method_block_impl_0(void *fp, struct __MCBlock__method_block_desc_0 *desc, int _var, __unsafe_unretained id _unsafe_obj, __strong id _strong_obj, int *_static_var, int flags=0) : var(_var), unsafe_obj(_unsafe_obj), strong_obj(_strong_obj), static_var(_static_var) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __MCBlock__method_block_func_0(struct __MCBlock__method_block_impl_0 *__cself) {
  int var = __cself->var; // bound by copy
  __unsafe_unretained id unsafe_obj = __cself->unsafe_obj; // bound by copy
  __strong id strong_obj = __cself->strong_obj; // bound by copy
  int *static_var = __cself->static_var; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hk_552cymzd23v5b0db528k_jbr0000gn_T_MCBlock_a2d5da_mi_0, var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hk_552cymzd23v5b0db528k_jbr0000gn_T_MCBlock_a2d5da_mi_1, unsafe_obj);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hk_552cymzd23v5b0db528k_jbr0000gn_T_MCBlock_a2d5da_mi_2, strong_obj);

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hk_552cymzd23v5b0db528k_jbr0000gn_T_MCBlock_a2d5da_mi_3, (*static_var));
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hk_552cymzd23v5b0db528k_jbr0000gn_T_MCBlock_a2d5da_mi_4, global_var);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_hk_552cymzd23v5b0db528k_jbr0000gn_T_MCBlock_a2d5da_mi_5, static_global_var);
    }
static void __MCBlock__method_block_copy_0(struct __MCBlock__method_block_impl_0*dst, struct __MCBlock__method_block_impl_0*src) {_Block_object_assign((void*)&dst->unsafe_obj, (void*)src->unsafe_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->strong_obj, (void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __MCBlock__method_block_dispose_0(struct __MCBlock__method_block_impl_0*src) {_Block_object_dispose((void*)src->unsafe_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->strong_obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __MCBlock__method_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __MCBlock__method_block_impl_0*, struct __MCBlock__method_block_impl_0*);
  void (*dispose)(struct __MCBlock__method_block_impl_0*);
} __MCBlock__method_block_desc_0_DATA = { 0, sizeof(struct __MCBlock__method_block_impl_0), __MCBlock__method_block_copy_0, __MCBlock__method_block_dispose_0};

static void _I_MCBlock_method(MCBlock * self, SEL _cmd) {

    int var = 1;


    __attribute__((objc_ownership(none))) id unsafe_obj = __null;
    __attribute__((objc_ownership(strong))) id strong_obj = __null;

    static int static_var = 3;

    void(*Block)(void) = ((void (*)())&__MCBlock__method_block_impl_0((void *)__MCBlock__method_block_func_0, &__MCBlock__method_block_desc_0_DATA, var, unsafe_obj, strong_obj, &static_var, 570425344));

    ((void (*)(__block_impl *))((__block_impl *)Block)->FuncPtr)((__block_impl *)Block);
}

// @end

__block修飾符

  • 一般情況下都许,對(duì)被截獲變量進(jìn)行賦值操作需添加__block修飾符
  • 對(duì)靜態(tài)局部變量稻薇,全局變量,靜態(tài)全局變量進(jìn)行賦值時(shí)候胶征,不需要__block修飾符
    {
        NSMutableArray *array = [NSMutableArray array];
        void(^Block)(void) = ^{
            [array addObject:@123];
        };
        Block();
    }

上面的array是否需要__block修飾塞椎?

上面只是對(duì)array進(jìn)行了使用,而不是賦值操作睛低,只有賦值操作才需要

__block修飾的變量變成了對(duì)象

{
    __block int multiplier = 6;
    int(^Block)(int) = ^int(int num)
    {
        return num * multiplier;
    };
    multiplier = 4;
    Block(2); //8
}
__block修飾.png
棧區(qū)__block.png

所以上面的代碼multiplier = 4;實(shí)際上變成了(multiplier._forwarding->multiplier) = 4;

Block的類型

  • _NSConcreteGlobalBlock
  • _NSConcreteStackBlock
  • _NSConcreteMallocBlock
Block位置.png

Block的Copy操作

假如聲明一個(gè)對(duì)象的成員變量是Block案狠,而在棧上去創(chuàng)建Block,同時(shí)賦值給成員變量的Block钱雷,如果成員變量的Block沒有使用copy關(guān)鍵字的話骂铁,當(dāng)我們具體通過成員變量去訪問對(duì)應(yīng)的Block,可能就由于棧上的Block被釋放而引起崩潰罩抗。

棧上Block的銷毀

block的銷毀.png

棧上Block的copy

棧上Block的copy.png

在MRC環(huán)境拉庵,假如對(duì)棧上Block進(jìn)行copy操作,Block會(huì)引起內(nèi)存泄漏套蒂,因?yàn)锽lock copy之后钞支,堆上Block沒有額外的成員變量去指向它,就像對(duì)一個(gè)對(duì)象進(jìn)行alloc操作之后操刀,沒有調(diào)用對(duì)應(yīng)的release的效果是一樣的,產(chǎn)生內(nèi)存泄漏烁挟。

棧上Block的copy02.png

棧上Block進(jìn)行copy之后,__forwarding指針是指向堆上的__block變量骨坑,堆上的Block__forwarding指針指向自身信夫。

__forwarding總結(jié)

//百度面試題
{
        __block int multiplier = 10;
        _blk = ^int(int num) {
            return num * multiplier;
        };
        multiplier = 6;
        [self executeBlock];
}
- (void)executeBlock
{
    int result = _blk(4);
    NSLog(@"result is %d", result); //result is 24
}

__forwarding存在的意義

不論在任何內(nèi)存位置,都可以順利的訪問同一個(gè)__block變量卡啰。

Block的引用循環(huán)

__block的引用循環(huán)

{
   __block MCBlock *blockSelf = self;
        _blk = ^int(int num) {
          //var = 2
            return num * blockSelf.var;
        };
        _blk(3);
}
  • 在MRC下,不會(huì)產(chǎn)生引用循環(huán)警没。
  • 在ARC下匈辱,會(huì)產(chǎn)生循環(huán)引用,引起內(nèi)存泄漏杀迹。?
__block的循環(huán)引用.png
{
   __block MCBlock *blockSelf = self;
   _blk = ^int(int num) {
          //var = 2
          int result = num * blockSelf.var;
          blockSelf = nil;
          return result;
    };
    _blk(3);//如果Block長時(shí)間等不到調(diào)用亡脸,則引用循環(huán)會(huì)存在。
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末树酪,一起剝皮案震驚了整個(gè)濱河市浅碾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌续语,老刑警劉巖垂谢,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疮茄,居然都是意外死亡滥朱,警方通過查閱死者的電腦和手機(jī)根暑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來徙邻,“玉大人排嫌,你說我怎么就攤上這事$掷纾” “怎么了淳地?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長帅容。 經(jīng)常有香客問我颇象,道長,這世上最難降的妖魔是什么丰嘉? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任夯到,我火速辦了婚禮,結(jié)果婚禮上饮亏,老公的妹妹穿的比我還像新娘耍贾。我一直安慰自己,他們只是感情好路幸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布荐开。 她就那樣靜靜地躺著,像睡著了一般简肴。 火紅的嫁衣襯著肌膚如雪晃听。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天砰识,我揣著相機(jī)與錄音能扒,去河邊找鬼。 笑死辫狼,一個(gè)胖子當(dāng)著我的面吹牛初斑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播膨处,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼见秤,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了真椿?” 一聲冷哼從身側(cè)響起鹃答,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎突硝,沒想到半個(gè)月后测摔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡狞换,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年避咆,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舟肉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡查库,死狀恐怖路媚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情樊销,我是刑警寧澤整慎,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站围苫,受9級(jí)特大地震影響裤园,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜剂府,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一拧揽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腺占,春花似錦淤袜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至意鲸,卻和暖如春烦周,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怎顾。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工读慎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人槐雾。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓贪壳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蚜退。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容