下文所有的讨论都基于ARC
如有错误或不严谨,请多多赐教.感激不尽
1.什么是Block?
block是"带有自动变量值的匿名函数"
2.ARC下三种block都是什么?
__NSGlobalBlock__
-- 类似函数,位于text段
__NSMallocBlock__
-- 堆区
__NSStackBlock__
-- 栈区
3.三种block分别在什么情况下出现?
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做方法参数
- (void)demo:(void(^)())block {
NSLog(@"%@",block);
}
- (void)viewDidLoad {
[super viewDidLoad];
[self demo:^{
NSLog(@"%d",a);
}];
}
一定是在调用方法传递参数时实现block才是NSStackBlock,如果事先实现好的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的原理是什么?
结论: 把原本以值传递传递到block中的变量换成引用传递传递到block内部.
结论怎么来的,下面开始解释.
第一个测试代码
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
的内存地址, 所以可以改变变量的值.
5.修改什么样的变量需要使用__block,什么样的不需要?
先说结论:
需要加__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的回调函数.由函数参数可以看出来,即使是没有参数的block,实际运行的时候也会接受一个__DemoClass__demo_block_impl_0
的结构体,结构体代码如下:
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;
}
};
从结构体中可以看出,此时的block内部可以获取到self
如①所示,之所以修改属性不需要__block
是因为block
中可以获取到self
,而通过self
和objc_msgSend
就可以调用该属性的set
和get
方法去赋值.
成员变量呢?
如③所示,myNumB = 10
被编译成(*(int *)((char *)self + OBJC_IVAR_$_DemoClass$myNumB)) = 10
.
我们可以近似的看成(*(int *))myNumB = 10
.可以看出,myNumB
的值也是通过修改其内存地址里面的内容赋值的,而赋值也需要self
.
结合②③很明显可以看出,成员变量的赋值和访问也是通过self
去赋值的,只不过没有set
和get方法
.
2.经过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;
}
此时的结构体中获取到的是myNumA
的内存地址,所以不需要__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));
}
这时可以看到,结构体中自动捕获了int *myNumA;NSMutableArray *arrM;
.都是指针.所以也不需要__block.