## Block拷贝
block的属性需要使用copy修饰,如果block一旦没有copy操作就不会在堆上,无法对block生命周期进行控制。
使用注意:循环引用的问题。
block在修改NSMutableArray,不需要添加__block,因为修改内容也是对数组的使用,只有对对象赋值的时候才需要__block。
## __Block
当使用__block修饰符时,基本数据类型 a 被转换成了__Block_byref_a_0结构体。__Block_byref_a_0结构体中带有 isa指针,说明它也是一个对象。
当block修改变量时,会调用下面代码:
__block修饰变量用结构体成员变量__forwarding可以实现无论 __block变量配置在栈上还是堆上都能够正确的访问 __block变量。
__forwarding指针始终指向自己。
当__block int a在栈中的时候,__forwarding指向的是栈中的自己。
当 a 拷贝到堆中时候,__forwarding指向的是堆中的自己。
__block 截获自动变量是实质就是:即截获变量指针。自动变量的值被保存到block结构体实例(Block本身)中。
这个结构体名字叫:__main_block_impl_0。
## 链式编程
### ReactiveCocoa
### Masonry
### RxSwift
### SnapKit
## 类型
注意:在 ARC 开启的情况下,将只会有 NSConcreteGlobalBlock 和 NSConcreteMallocBlock 类型的 block。因为Block从栈拷贝到堆中。
### NSConcreteGlobalBlock:block内部没有访问外部auto变量.
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __Block_byref_obj_1 {
void *__isa;
__Block_byref_obj_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj;
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
__Block_byref_obj_1 *obj = __cself->obj; // bound by ref
(a->__forwarding->a)++;
(obj->__forwarding->obj) = ((NSMutableArray * _Nonnull (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("array"));
printf("截获后--:%d", (a->__forwarding->a));
}
NSGlobalBlock是位于全局区的block,它是设置在程序的数据区域(.data区)中。
以下两种情况下,block为NSConcreteGlobalBlock
a.记述全局变量的地方有block语法时。
b.block语法的表达式中不使用任何外部变量时。
- 全局区block底层原理
struct __block_impl {
void *isa;//指向所属类的指针,也就是block的类型
int Flags;//标志变量,在实现block的内部操作时会用到
int Reserved;//保留变量
void *FuncPtr;//block执行时调用的函数指针
};
struct __globalBlock_block_impl_0 {
struct __block_impl impl;
struct __globalBlock_block_desc_0* Desc;
__globalBlock_block_impl_0(void *fp, struct __globalBlock_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteGlobalBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __globalBlock_block_func_0(struct __globalBlock_block_impl_0 *__cself) {
static int g = 10;
printf("%d",g);
}
static struct __globalBlock_block_desc_0 {
size_t reserved;
size_t Block_size;
} __globalBlock_block_desc_0_DATA = { 0, sizeof(struct __globalBlock_block_impl_0)};
static __globalBlock_block_impl_0 __global_globalBlock_block_impl_0((void *)__globalBlock_block_func_0, &__globalBlock_block_desc_0_DATA);
void (*globalBlock)() = ((void (*)())&__global_globalBlock_block_impl_0);
int main(int argc, const char * argv[]) {
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
### NSConcreteStackBlock
NSStackBlock是位于栈区,超出变量作用域,栈上的Block以及 __block变量都被销毁。只在MRC存在,ARC不存在,ARC只要是block内部访问量而且有强引用指向该block(或者作为函数返回值)就会自动将__NSStackBlock类型copy到堆上。
- 栈区block底层原理
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;//这个是C++的结构体 该函数是C++结构体的初始化函数
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
static struct __main_block_desc_0 {//这是对__main_block_impl_0结构体的描述信息
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
printf("Hello, World!\n");
int a = 10;
printf("%d", a);
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
### NSConcreteMallocBlock
NSMallocBlock是位于堆区,在变量作用域结束时不受影响。保存在堆中的 block,当引用计数为 0 时会被销毁。ARC环境下只要访问了外部auto变量而且有强引用指向该block(或者作为函数返回值)就会自动将__NSStackBlock类型copy到堆上。
堆中的block无法直接创建,其需要由_NSConcreteStackBlock类型的block拷贝而来(也就是说block需要执行copy之后才能存放到堆中)。由于block的拷贝最终都会调用_Block_copy_internal函数。
- 堆区block底层原理
__block int a = 10;
printf("截获前:%d", a);
void (^block)(void) = [^{
a++;
printf("截获后--:%d", a);
} copy];
a++;
printf("截获后:%d", a);
block();
//这样的话block的isa就是NSConcreteMallocBlock
typedef void (*blk_t)(void);
struct __getBlockArray0_block_impl_0 {
struct __block_impl impl;
struct __getBlockArray0_block_desc_0* Desc;
int val;
__getBlockArray0_block_impl_0(void *fp, struct __getBlockArray0_block_desc_0 *desc, int _val, int flags=0) : val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __getBlockArray0_block_func_0(struct __getBlockArray0_block_impl_0 *__cself) {
int val = __cself->val; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_0,val);}
static struct __getBlockArray0_block_desc_0 {
size_t reserved;
size_t Block_size;
} __getBlockArray0_block_desc_0_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_0)};
struct __getBlockArray0_block_impl_1 {
struct __block_impl impl;
struct __getBlockArray0_block_desc_1* Desc;
int val;
__getBlockArray0_block_impl_1(void *fp, struct __getBlockArray0_block_desc_1 *desc, int _val, int flags=0) : val(_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __getBlockArray0_block_func_1(struct __getBlockArray0_block_impl_1 *__cself) {
int val = __cself->val; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_77__vw6s40s54v9p9d9zb4f_3br0000gn_T_main_c1dee6_mi_1,val);}
static struct __getBlockArray0_block_desc_1 {
size_t reserved;
size_t Block_size;
} __getBlockArray0_block_desc_1_DATA = { 0, sizeof(struct __getBlockArray0_block_impl_1)};
NSArray *getBlockArray0() {
int val =10;
return ((NSArray * _Nonnull (*)(id, SEL, ObjectType _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSArray"), sel_registerName("arrayWithObjects:"), (id _Nonnull)((void (*)())&__getBlockArray0_block_impl_0((void *)__getBlockArray0_block_func_0, &__getBlockArray0_block_desc_0_DATA, val)), ((void (*)())&__getBlockArray0_block_impl_1((void *)__getBlockArray0_block_func_1, &__getBlockArray0_block_desc_1_DATA, val)), __null);
}
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
(a->__forwarding->a)++;
printf("截获后--:%d", (a->__forwarding->a));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
printf("截获前:%d", (a.__forwarding->a));
void (*block)(void) = (void (*)())((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344)), sel_registerName("copy"));
(a.__forwarding->a)++;
printf("截获后:%d", (a.__forwarding->a));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
## Block的本质
block本质上也是一个OC对象,它内部也有个isa指针
block是封装了函数调用以及函数调用环境的OC对象
## 引用循环
如果在Block中使用附有__strong修饰符的对象类型的自动变量,那么当Block从栈复制到堆时,该对象为Block所持有。这样容易引起循环引用。
在ARC环境下,只要将block赋值给一个变量,那么这个block就将被拷贝到堆上,这是编译器的优化。
- __weak
Member *member = [[Member alloc] init];
member.height = 120;
__weak typeof(member) weakMember = member;
member.didTappedBlock = ^{
printf("member height == %f\n", weakMember.height);
};
- __block
__block Member *member = [[Member alloc] init];
member.height = 120;
member.didTappedBlock = ^{
printf("member height == %f\n", member.height);
member = nil;
};
member.didTappedBlock();
1.必须在Block内使对象置为nil
2.必须调用Block,如果不调用Block,永远存在循环引用
- __unsafe_unretained
Member *member = [[Member alloc] init];
member.height = 120;
__unsafe_unretained typeof(member) weakMember = member;
member.didTappedBlock = ^{
printf("member height == %f\n", weakMember.height);
};
在MRC环境下:
1.MRC不支持__weak弱引用
2.MRC __block不会对对象产生引用(retain)
- __unsafe_unretained
Member *member = [[Member alloc] init];
member.height = 120;
__unsafe_unretained typeof(member) weakMember = member;
member.didTappedBlock = ^{
printf("member height == %f\n", weakMember.height);
};
NSLog(@"end=================%@==",[member.didTappedBlock class]);
[member release];
- __block
__block Member *member = [[Member alloc] init];
member.height = 120;
member.didTappedBlock = ^{
printf("member height == %f\n", member.height);
};
NSLog(@"end=================%@==",[member.didTappedBlock class]);
[member release];
例外可以传入一个局部变量,这样就不会导致引用循环了。
## 截获自动变量
这个其实也是block底层原理
利用__block
有三种情况不用__block也可以截获变量的值:
静态全局变量
全局变量
静态变量
*XMind: ZEN - Trial Version*