本文属笔记性质,主要针对本身理解不太透彻的地方进行记录。ios
推荐系统直接学习小码哥iOS底层原理班---MJ老师的课确实不错,强推一波。c++
只有在末尾执行
()
才会调用bash
^{
NSLog(@"this is a block!");
NSLog(@"this is a block!");
NSLog(@"this is a block!");
NSLog(@"this is a block!");
};
复制代码
// 不带参数的block。^(){能够简化成^{
void (^block)() = ^(){
};
block();
// 带参数的block
void (^block)(int, int) = ^(int a , int b){
NSLog(@"a=%d,b=%d",a,b);
};
block(20, 10);
复制代码
对于void (^block)(int, int)
的含义是返回值 (^名称)(参数1, 参数2)
函数
封装了函数调用以及调用环境的OC对象学习
int age = 20;
void (^block)(int, int) = ^(int a , int b){
NSLog(@"this is a block! -- %d", age);
};
block(10, 10);
复制代码
block最终将会被转化成以下格式的结构体(每一个block的细节不一样)ui
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc; //block描述信息(大小等)
int age; //封装了函数调用所需环境(内部定义了一个age变量)
//c++的构造函数 age(_age)表示_age将会自动赋值给age
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int age = __cself->age; // 将block当初捕获的变量,赋值给执行函数
//函数调用
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age);
}
struct __block_impl {
void *isa; //代表block也属于OC对象
int Flags;
int Reserved;
void *FuncPtr;
};
复制代码
其中函数调用会被单独封装成__main_block_func_0
方法。在block定义时,传入block结构体。this
void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
复制代码
而在block结构体中,会被赋值给impl.FuncPtr = fp;
,将函数地址存储在block内部。spa
最终,在调用block时,获取FuncPtr
,传入参数执行调用。3d
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
复制代码
构建一个block结构体指针
void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
/**
去掉强转的语法以后
将函数地址,block描述(大小等)信息,须要被捕获的变量。
传入构建block的方法`__main_block_impl_0`中进行构建
最后将`__main_block_impl_0`block结构体返回,并将其持有
**/
void (*block)(int, int) = &__main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, age));
复制代码
调用block结构体
((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 10, 10);
/**
去掉强转的语法以后
从结构体中取出函数地址,传入参数并调用。
传入blcok是由于每个block对象内部所捕获的变量不一样
**/
block->FuncPtr(block, 10, 10);
复制代码
为了保证block内部可以正常访问外部的变量,block有个变量捕获机制
c语言中的局部变量,默认都为auto变量。因此auto代指局部变量
因为auto变量的生命超出做用域就会被销毁。为保证block可以正常执行,auto变量在被block捕获时,会将
值
传递给block的构造函数。
int main(int argc, const char * argv[]) {
@autoreleasepool {
auto int a = 10;
void (^block)(void) = ^{
NSLog(@"age is %d", a); //age is 10
};
a = 20;
block();
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc; //block信息(大小等)
int age; //封装了函数调用所需环境(内部定义了一个age变量)
//c++的构造函数 age(_age)表示_age将会自动赋值给age
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int age = __cself->age; // 将block当初捕获的变量,赋值给执行函数
//函数调用
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_3f4c4a_mi_0, age);
}
// main中构建block。将a的值传递给构造函数
void (*block)() = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, a));
复制代码
因为静态变量的生命常驻于内存,但使用仅限于做用域内部。因此静态变量在被block捕获时,只要将指向变量值的指针(地址)传递给构造函数,即能保证block正常执行。
int main(int argc, const char * argv[]) {
@autoreleasepool {
static int b = 10;
void (^block)(void) = ^{
NSLog(@"height is %d", b); //height is 20
};
b = 20;
block();
}
return 0;
}
struct __test_block_impl_0 {
struct __block_impl impl;
struct __test_block_desc_0* Desc;
int *b; //b为指针类型
__test_block_impl_0(void *fp, struct __test_block_desc_0 *desc, int *_b, int flags=0) : b(_b) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __test_block_func_0(struct __test_block_impl_0 *__cself) {
int *b = __cself->b; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_fd2a14_mi_0, (*b));
}
// main中构建block.将b的指针传递给构造函数
void (*block)() = ((void (*)())&__test_block_impl_0((void *)__test_block_func_0, &__test_block_desc_0_DATA, &b));
复制代码
因为全局变量的生命常驻于内存,而且使用不受做用域限制。因此全局变量并不须要被捕获,在执行block的函数调用时,直接使用全局变量,即能保证block正常执行。
int age_ = 10;
static int height_ = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"age_ is %d ,height_ is %d", age_,height_);
};
age_ = 20;
height_ = 20;
block();
}
return 0;
}
int age_ = 10;
static int height_ = 10;
// block结构体内部,也并未声明对应的变量
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;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 执行函数中,直接使用全局变量
NSLog((NSString *)&__NSConstantStringImpl__var_folders_tz_hcmmb5t57v1cr81ydm6s5s140000gn_T_main_6f323a_mi_0, age_,height_);
}
static struct __main_block_desc_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[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); //在执行block构造函数时,并未将全局变量传递进去
age_ = 20;
height_ = 20;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
复制代码
经过class方法,也能够证实block的本质是OC对象
void test()
{
void (^block)(void) = ^{
NSLog(@"Hello");
};
NSLog(@"%@", [block class]);//__NSGlobalBlock__ (其余类型的block为__NSStackBlock__或__NSMallocBlock__)
NSLog(@"%@", [[block class] superclass]);//__NSGlobalBlock
NSLog(@"%@", [[[block class] superclass] superclass]);//NSBlock
NSLog(@"%@", [[[[block class] superclass] superclass] superclass]);//NSObject
}
复制代码
在block结构体中,isa会被指定成
_NSConcreteXXXBlock
的值。这个值即是block的类型。
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *obj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
复制代码
block有3种类型,能够经过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
NSGlobalBlock ( _NSConcreteGlobalBlock )
NSStackBlock ( _NSConcreteStackBlock )
NSMallocBlock ( _NSConcreteMallocBlock )
从上到下为:低地址 --》高地址 非ARC下的block来源
在非ARC下
绝大部分
block
默认都是做为__NSStackBlock__
存在于栈上(没有访问auto变量的block会存在于全局区)。而
__NSStackBlock__
在超出做用域后,block结构体有可能会被修改污染。这时须要将
__NSStackBlock__
进行copy
将其转移到堆中进行管理。以后
__NSStackBlock__
将转变为__NSMallocBlock__
这也是为何block做为属性都会声明为copy(不过ARC下声明为strong也会自动copy,文档上说是为了语义)
对于不一样类型的block,调用copy会有不一样操做
在ARC环境下,编译器会根据状况自动将栈上的block复制到堆上,好比如下状况
MJBlock myblock()
{
return ^{
NSLog(@"---------");
};
}
复制代码
MJBlock block = ^{
NSLog(@"---------%d", age);
};
复制代码
[@[] enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
复制代码
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
复制代码
(这里都说ARC环境下)
在block捕获对象类型的auto变量时。
__main_block_desc_0
结构体内部会多出__main_block_copy_0
和__main_block_dispose_0
函数,在block移动到堆空间时堆对象进行适当的return和release。
先贴一份cpp代码(auto变量)
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [NSObject new]; //OC对象
void (^block)(void) = ^{
NSLog(@"obj is %@", obj); //捕获
};
obj = nil;
block();
}
return 0;
}
//block结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
//若是外部为__weak内部也会为__weak
NSObject * __strong obj; //对于auto变量,结构体中保存变量与本来相同。
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSObject *obj = __cself->obj; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_tz_hcmmb5t57v1cr81ydm6s5s140000gn_T_main_d73acf_mi_0, obj);
}
//当block从栈移动到堆中时,执行此方法。
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {
//会根据auto变量的修饰符(__strong、__weak、__unsafe_unretained)作出相应的操做,造成强引用(retain)或者弱引用
_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
//当block从堆中移除(释放)时,执行此方法。
static void __main_block_dispose_0(struct __main_block_impl_0*src) {
//release引用的变量
_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);
}
//block描述。比基本类型多了两个变量。就是上面两个方法
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};
// main函数
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("new"));
//在构造block时,将OC对象传入
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
复制代码
基本也符合上面的规律,以局部变量为例。
只是对于对象而言,传递的都是指针。 并且为了保证OC对象在block内部正常访问,会被强引用以延长对象生命周期。
若是block是在栈上,将不会对auto变量产生强引用
会执行内部的
_Block_object_assign
函数,结构体内所捕获的auto对象的类型(__strong/__weak)决定如何对其进行引用。
会执行内部的
_Block_object_dispose
函数,将结构体内所捕获的auto对象进行release。
编译器会将__block变量包装成一个对象(
__Block_byref_age_0
),被声明的值做为对象的属性存在。
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
MJBlock block = ^{
__strong int myage = age;
age = 20;
NSLog(@"age is %d", age);
};
block();
}
return 0;
}
//__block对象结构体
struct __Block_byref_age_0 {
void *__isa;
__Block_byref_age_0 *__forwarding;
int __flags;
int __size;
int age;
};
//block结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *p;
//再也不是int age;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_p, __Block_byref_age_0 *_age, int flags=0) : p(_p), age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// block执行函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
//从__Block_byref_age_0结构体中得到age变量,而且修改
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_2r__m13fp2x2n9dvlr8d68yry500000gn_T_main_e2457b_mi_0);
}
//c++ main函数
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
//__block int age = 10;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
//等价于
__Block_byref_age_0 age = {0,
&age,
0,
sizeof(__Block_byref_age_0),
10};
//构建block结构体
MJBlock block = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, p, (__Block_byref_age_0 *)&age, 570425344));
//等价于
MJBlock block = &__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA,
p,
&age,
570425344);
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
复制代码
实际上都是对结构体中的age指针进行操做,而不是结构体age。
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int age = 10;
age = 50;
}
return 0;
}
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
(age.__forwarding->age) = 50;
}
return 0;
}
复制代码
__forwarding
指向__block
所包装的对象实体,以确保使用时的正确性。
__forwarding
所指实体为栈上的__block
包装对象。__block
包装对象在堆中也会被复制一份。而两者的__forwarding
指针都指向堆中的__block
包装对象。OC对象的强弱引用不会体如今block结构体中(都是strong),而是体如今
__Block_byref
结构体中。
int main(int argc, const char * argv[]) {
@autoreleasepool {
MJPerson *person = [[MJPerson alloc] init];
__block __weak JSPerson *weakPerson = person;
MJBlock block = ^{
NSLog(@"%p", weakPerson);
};
block();
}
return 0;
}
struct __Block_byref_weakPerson_0 {
void *__isa; // 8
__Block_byref_weakPerson_0 *__forwarding; // 8
int __flags; // 4
int __size; // 4
void (*__Block_byref_id_object_copy)(void*, void*); // 当block被移动到堆,会对__Block_byref对象进行return(须要注意MRCblock进入堆中时不会retain该变量)
void (*__Block_byref_id_object_dispose)(void*); // 当block从堆中移除,会对__Block_byref对象进行release
MJPerson *__weak weakPerson; //__block包装的结构体中实际为weak引用。
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_weakPerson_0 *weakPerson; // // 依旧是strong引用
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_weakPerson_0 *_weakPerson, int flags=0) : weakPerson(_weakPerson->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
复制代码
三个已知的内存地址段
NSLog(@"数据段:age %p", &age);
NSLog(@"栈:a %p", &a);
NSLog(@"堆:obj %p", [[NSObject alloc] init]);
复制代码