下文所有的討論都基于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
,而通過self
和objc_msgSend
就可以調(diào)用該屬性的set
和get
方法去賦值.
成員變量呢?
如③所示,myNumB = 10
被編譯成(*(int *)((char *)self + OBJC_IVAR_$_DemoClass$myNumB)) = 10
.
我們可以近似的看成(*(int *))myNumB = 10
.可以看出,myNumB
的值也是通過修改其內(nèi)存地址里面的內(nèi)容賦值的,而賦值也需要self
.
結(jié)合②③很明顯可以看出,成員變量的賦值和訪問也是通過self
去賦值的,只不過沒有set
和get方法
.
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.