關(guān)于Block

下文所有的討論都基于ARC

如有錯誤或不嚴(yán)謹(jǐn),請多多賜教.感激不盡


1.什么是Block?

block是"帶有自動變量值的匿名函數(shù)"

2.ARC下三種block都是什么?

__NSGlobalBlock__ -- 類似函數(shù)饮六,位于text段
__NSMallocBlock__ -- 堆區(qū)
__NSStackBlock__ -- 棧區(qū)

3.三種block分別在什么情況下出現(xiàn)?

NSGlobalBlock(遇見情況較少)
沒有引用外部變量的block,此時的block為NSGlobalBlock

NSStackBlock

情況1

assign修飾,并且引用了外部變量

@interface ViewController ()
@property (assign, nonatomic) void(^myBlock1)();
@end
@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    int a = 10;
    self.myBlock1 = ^{
        NSLog(@"%d",a);
    };
}
@end
情況2

block做方法參數(shù)

- (void)demo:(void(^)())block {   
    NSLog(@"%@",block);
}
- (void)viewDidLoad {
    [super viewDidLoad];
    [self demo:^{
        NSLog(@"%d",a);
    }];
}

一定是在調(diào)用方法傳遞參數(shù)時實現(xiàn)block才是NSStackBlock,如果事先實現(xiàn)好的block,如
[self demo:self.myBlock1];
這樣傳遞來的block是和self.myBlock的block是同類型的

NSMallocBlock(最常見最常用的)

使用copy修飾并引用了外部變量,或直接聲明并引用了外部變量

@property (copy, nonatomic) void(^myBlock2)();
    self.myBlock2 = ^{
        
        NSLog(@"%d",a);
    };
    或
    void(^myBlock3)() = ^{
        
        NSLog(@"%@",arr);
    };

4.__block的原理是什么?

結(jié)論: 把原本以值傳遞傳遞到block中的變量換成引用傳遞傳遞到block內(nèi)部.
結(jié)論怎么來的,下面開始解釋.
第一個測試代碼

    int myNumA = 10;
    self.myBlock1 = ^{     
        myNumA++;
    };

這種寫法是會報錯的, 為什么報錯?

struct __DemoClass__demo_block_impl_0 {
    struct __block_impl impl;
    struct __DemoClass__demo_block_desc_0* Desc;
    int myNumA;
    __DemoClass__demo_block_impl_0(void *fp, struct __DemoClass__demo_block_desc_0 *desc, int _myNumA, int flags=0) : myNumA(_myNumA) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __DemoClass__demo_block_func_0(struct __DemoClass__demo_block_impl_0 *__cself) {
    int myNumA = __cself->myNumA; // bound by copy
    
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_97_s25cw5dj7ss2rf1q7_bchd7w0000gn_T_DemoClass_8ec7ba_mi_0,myNumA);
}

static struct __DemoClass__demo_block_desc_0 {
    size_t reserved;
    size_t Block_size;
} __DemoClass__demo_block_desc_0_DATA = { 0, sizeof(struct __DemoClass__demo_block_impl_0)};

static void _I_DemoClass_demo(DemoClass * self, SEL _cmd) {
    
    int myNumA = 10;
    
    void(*myBlock)() = ((void (*)())&__DemoClass__demo_block_impl_0((void *)__DemoClass__demo_block_func_0, &__DemoClass__demo_block_desc_0_DATA, myNumA));
}

這是編譯成的C++代碼, 所有myNumA都是值傳遞.所以,沒法修改是很顯然的.

    __block int myNumA = 10;
    self.myBlock1 = ^{     
        myNumA++;
    };

編譯之后,首先,對myNumA進行一個處理

struct __Block_byref_myNumA_0 {
    void *__isa;
    __Block_byref_myNumA_0 *__forwarding;
    int __flags;
    int __size;
    int myNumA;
};

其中有一個__Block_byref_myNumA_0 *__forwarding;指針.
接下來

struct __DemoClass__demo_block_impl_0 {
    struct __block_impl impl;
    struct __DemoClass__demo_block_desc_0* Desc;
    __Block_byref_myNumA_0 *myNumA; // by ref
    __DemoClass__demo_block_impl_0(void *fp, struct __DemoClass__demo_block_desc_0 *desc, __Block_byref_myNumA_0 *_myNumA, int flags=0) : myNumA(_myNumA->__forwarding) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
    }
};
static void __DemoClass__demo_block_func_0(struct __DemoClass__demo_block_impl_0 *__cself) {
    __Block_byref_myNumA_0 *myNumA = __cself->myNumA; // bound by ref
    
    
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_97_s25cw5dj7ss2rf1q7_bchd7w0000gn_T_DemoClass_29df03_mi_0,(myNumA->__forwarding->myNumA)++);
}
static void __DemoClass__demo_block_copy_0(struct __DemoClass__demo_block_impl_0*dst, struct __DemoClass__demo_block_impl_0*src) {_Block_object_assign((void*)&dst->myNumA, (void*)src->myNumA, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __DemoClass__demo_block_dispose_0(struct __DemoClass__demo_block_impl_0*src) {_Block_object_dispose((void*)src->myNumA, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __DemoClass__demo_block_desc_0 {
    size_t reserved;
    size_t Block_size;
    void (*copy)(struct __DemoClass__demo_block_impl_0*, struct __DemoClass__demo_block_impl_0*);
    void (*dispose)(struct __DemoClass__demo_block_impl_0*);
} __DemoClass__demo_block_desc_0_DATA = { 0, sizeof(struct __DemoClass__demo_block_impl_0), __DemoClass__demo_block_copy_0, __DemoClass__demo_block_dispose_0};

static void _I_DemoClass_demo(DemoClass * self, SEL _cmd) {
    
    __attribute__((__blocks__(byref))) __Block_byref_myNumA_0 myNumA = {(void*)0,(__Block_byref_myNumA_0 *)&myNumA, 0, sizeof(__Block_byref_myNumA_0), 10};
    
    void(*myBlock)() = ((void (*)())&__DemoClass__demo_block_impl_0((void *)__DemoClass__demo_block_func_0, &__DemoClass__demo_block_desc_0_DATA, (__Block_byref_myNumA_0 *)&myNumA, 570425344));
}

所有對myNumA的引用都變成了__Block_byref_myNumA_0 *myNumA.
在最后兩行,我們聲明的int myNumA = 10變成了__Block_byref_myNumA_0 myNumA = {(void*)0,(__Block_byref_myNumA_0 *)&myNumA, 0, sizeof(__Block_byref_myNumA_0), 10};,傳入進去的是(__Block_byref_myNumA_0 *)&myNumA,很明顯,傳遞的是myNumA的內(nèi)存地址, 所以可以改變變量的值.

5.修改什么樣的變量需要使用__block,什么樣的不需要?

先說結(jié)論:
需要加__block修飾的只有局部變量且非指針變量.
成員變量,屬性,static修飾過得局部變量,局部指針變量都不需要加__block修飾.

解釋原因

1.屬性和成員變量不需要加__block

測試代碼:

@interface DemoClass ()
@property (assign, nonatomic) int myNumA;
@end

@implementation DemoClass {
    int myNumB;
}

- (void)demo {
    self.myNumA = 10;
    myNumB = 10;
    void(^myBlock)() = ^{
        self.myNumA = self.myNumA + 1;
        myNumB = myNumB + 1;
    };
    myBlock();
}

編譯后的C++代碼:

1.static void __DemoClass__demo_block_func_0(struct __DemoClass__demo_block_impl_0 *__cself) {
        DemoClass *self = __cself->self; // bound by copy
       ① ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)self, sel_registerName("setMyNumA:"), ((int (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("myNumA")) + 1);
       ②(*(int *)((char *)self + OBJC_IVAR_$_DemoClass$myNumB)) = (*(int *)((char *)self + OBJC_IVAR_$_DemoClass$myNumB)) + 1;
}

2.static void _I_DemoClass_demo(DemoClass * self, SEL _cmd) {

    ((void (*)(id, SEL, int))(void *)objc_msgSend)((id)self, sel_registerName("setMyNumA:"), 10);
    ③ (*(int *)((char *)self + OBJC_IVAR_$_DemoClass$myNumB)) = 10;
    void(*myBlock)() = ((void (*)())&__DemoClass__demo_block_impl_0((void *)__DemoClass__demo_block_func_0, &__DemoClass__demo_block_desc_0_DATA, self, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
}

方法"1."是block的回調(diào)函數(shù).由函數(shù)參數(shù)可以看出來,即使是沒有參數(shù)的block,實際運行的時候也會接受一個__DemoClass__demo_block_impl_0的結(jié)構(gòu)體,結(jié)構(gòu)體代碼如下:

struct __DemoClass__demo_block_impl_0 {
  struct __block_impl impl;
  struct __DemoClass__demo_block_desc_0* Desc;
  DemoClass *self;
  __DemoClass__demo_block_impl_0(void *fp, struct __DemoClass__demo_block_desc_0 *desc, DemoClass *_self, int flags=0) : self(_self) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

從結(jié)構(gòu)體中可以看出,此時的block內(nèi)部可以獲取到self
如①所示,之所以修改屬性不需要__block是因為block中可以獲取到self,而通過selfobjc_msgSend就可以調(diào)用該屬性的setget方法去賦值.
成員變量呢?
如③所示,myNumB = 10被編譯成(*(int *)((char *)self + OBJC_IVAR_$_DemoClass$myNumB)) = 10.
我們可以近似的看成(*(int *))myNumB = 10.可以看出,myNumB的值也是通過修改其內(nèi)存地址里面的內(nèi)容賦值的,而賦值也需要self.
結(jié)合②③很明顯可以看出,成員變量的賦值和訪問也是通過self去賦值的,只不過沒有setget方法.
2.經(jīng)過static修飾過的局部變量
測試代碼如下:

- (void)demo {
    static int myNumA = 10;
    void(^myBlock)() = ^{
        myNumA = myNumA + 1;
    };
    myBlock();
}

編譯后的C++代碼如下:

struct __DemoClass__demo_block_impl_0 {
  struct __block_impl impl;
  struct __DemoClass__demo_block_desc_0* Desc;
  int *myNumA;
  __DemoClass__demo_block_impl_0(void *fp, struct __DemoClass__demo_block_desc_0 *desc, int *_myNumA, int flags=0) : myNumA(_myNumA) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __DemoClass__demo_block_func_0(struct __DemoClass__demo_block_impl_0 *__cself) {
  int *myNumA = __cself->myNumA; // bound by copy

        (*myNumA) = (*myNumA) + 1;
}

此時的結(jié)構(gòu)體中獲取到的是myNumA的內(nèi)存地址,所以不需要__block修飾.

3.局部指針變量

測試代碼如下:

- (void)demo {
    int a = 10;
    int *myNumA = &a;
    NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@1, @2, nil];
    void(^myBlock)() = ^{
        *myNumA = *myNumA + 1;
        [arrM addObject:@3];
    };
    myBlock();
}

編譯后的C++代碼:

struct __DemoClass__demo_block_impl_0 {
  struct __block_impl impl;
  struct __DemoClass__demo_block_desc_0* Desc;
  int *myNumA;
  NSMutableArray *arrM;
  __DemoClass__demo_block_impl_0(void *fp, struct __DemoClass__demo_block_desc_0 *desc, int *_myNumA, NSMutableArray *_arrM, int flags=0) : myNumA(_myNumA), arrM(_arrM) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __DemoClass__demo_block_func_0(struct __DemoClass__demo_block_impl_0 *__cself) {
  int *myNumA = __cself->myNumA; // bound by copy
  NSMutableArray *arrM = __cself->arrM; // bound by copy

        *myNumA = *myNumA + 1;
        ((void (*)(id, SEL, ObjectType))(void *)objc_msgSend)((id)arrM, sel_registerName("addObject:"), (id)((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 3));
}

這時可以看到,結(jié)構(gòu)體中自動捕獲了int *myNumA;NSMutableArray *arrM;.都是指針.所以也不需要__block.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末没咙,一起剝皮案震驚了整個濱河市垦梆,隨后出現(xiàn)的幾起案子喇喉,更是在濱河造成了極大的恐慌返劲,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件敞嗡,死亡現(xiàn)場離奇詭異软啼,居然都是意外死亡,警方通過查閱死者的電腦和手機瓦盛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進店門洗显,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人原环,你說我怎么就攤上這事挠唆。” “怎么了嘱吗?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵玄组,是天一觀的道長。 經(jīng)常有香客問我,道長俄讹,這世上最難降的妖魔是什么哆致? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮患膛,結(jié)果婚禮上摊阀,老公的妹妹穿的比我還像新娘。我一直安慰自己踪蹬,他們只是感情好胞此,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著跃捣,像睡著了一般豌鹤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枝缔,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天布疙,我揣著相機與錄音,去河邊找鬼愿卸。 笑死灵临,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的趴荸。 我是一名探鬼主播儒溉,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼发钝!你這毒婦竟也來了顿涣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤酝豪,失蹤者是張志新(化名)和其女友劉穎涛碑,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體孵淘,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡蒲障,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了瘫证。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片揉阎。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖背捌,靈堂內(nèi)的尸體忽然破棺而出毙籽,到底是詐尸還是另有隱情,我是刑警寧澤毡庆,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布坑赡,位于F島的核電站巡扇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏垮衷。R本人自食惡果不足惜厅翔,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望搀突。 院中可真熱鬧刀闷,春花似錦、人聲如沸仰迁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽徐许。三九已至施蜜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間雌隅,已是汗流浹背翻默。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留恰起,地道東北人修械。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像检盼,于是被迫代替她去往敵國和親肯污。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

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