block
的本质
-
block本质
上也是一个OC对象
,它内部
也有个isa
指针。 -
block
是封装了函数调用
以及函数调用环境
的OC
对象。 - block的底层结构如右图所示:
block
的变量捕获(capture)
-
为了保证
block内部
能够正常访问外部的变量
,block
有个变量捕获机制。
1.
局部变量
-auto
{
auto int age = 10;//auto可以省略
void(^block)(void)=^{
//age的值捕获进来(capture)
NSLog(@"age is %d,height is %d",age,height);
};
age = 20;
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;
int 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;
// age(_age) C++语法 ==> age = _age;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//变量捕获
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hl_g93_s7nx05zdzh42fv2fp4rw0000gn_T_main_04a8a7_mi_0,age);
}
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 age = 10;
void(*block) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
age);
age = 20;
block->FuncPtr(block);
}
- 2.
局部变量
-static
{
static int height = 10;
void(^block)(void)=^{
//height的值捕获进来(capture)
NSLog(@"height is %d",height);
};
height = 20;
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;
int *height;//捕获到的指针
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_height, int flags=0) : height(_height) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
// age(_age) C++语法 ==> age = _age;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//变量捕获
int *height = __cself->height; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_hl_g93_s7nx05zdzh42fv2fp4rw0000gn_T_main_04a8a7_mi_0,(*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)};
}
{
static int height = 10;
void(*block) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA,
&height);
height = 20;
block->FuncPtr(block);
}
- 3.全局变量
block的类型
- block有
3种
类型,可以通过调用class方法
或者isa指针
查看具体类型,最终都是继承自NSBlock类型
.
1、__ NSGlobalBlock __ ( _ NSConcreteGlobalBlock )
2、__ NSStackBlock __ ( _ NSConcreteStackBlock )
3、__ NSMallocBlock __ ( _ NSConcreteMallocBlock )
- 没有访问
auto
变量
即使访问了
static
或全局变量
,block
的类型
仍然是__ NSGlobalBlock__
。
int age = 10;
int main(int argc, const char * argv[]) {
@autoreleasepool {
void (^block)(void) = ^{
NSLog(@"age----%d",age);
};
static int a = 100;
void (^block1)(void) = ^{
NSLog(@"a----%d",a);
};
NSLog(@"block'type is %@;block1'type is %@",[block class],[block1 class]);
}
return 0;
}
控制台打印的结果:2020-07-05 23:31:53.484623+0800 interview4-block的类型[5014:424923] block'type is __NSGlobalBlock__;block1'type is __NSGlobalBlock__
- 访问
auto
变量
说明:
1、在ARC
环境下的block
类型是__ NSMallocBlock__
(因为运行时Runtime
做了太多的事导致访问了auto
变量之后block
是NSMallocBlock
类型的block
);
2、在MRC
环境下的类型是__ NSMallocBlock__
。
`ARC`环境下:`__ NSMallocBlock__ `
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 100;
void (^block1)(void) = ^{
NSLog(@"a----%d",a);
};
NSLog(@"block1'type is %@",[block1 class]);
}
return 0;
}
MRC
环境下的类型是__ NSStackBlock __
。
-
__ NSStackBlock __
调用了copy
返回的block类型是__ NSMallocBlock__
。
说明:不管是
ARC
或MRC
,__ NSStackBlock __
类型的block
只要调用了copy
返回的类型都是__ NSMallocBlock__
。
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 100;
void (^block1)(void) = ^{
NSLog(@"a----%d",a);
};
NSLog(@"block1'type is %@",[[block1 copy] class]);
}
return 0;
}
- 每一种类型的block调用copy后的结果如下所示:
Block
的copy
- 在
ARC
环境下,编译器
会根据情况自动将栈上
的block复制到堆上
,比如以下情况:
1.block
作为函数返回值
时。
#import <Foundation/Foundation.h>
typedef void(^KXBlock)(void);
KXBlock myBlock(){
/**
1.ARC环境下return时,会自动为block 调用一次copy操作,[block copy];
2.MRC环境下return时,开发人员需要自己调用copy,[block copy];和[block release]操作。
*/
KXBlock block = ^{
NSLog(@"-------------");
};
return block;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
KXBlock block = myBlock();
block();
NSLog(@"%@",[block class]);
}
return 0;
}
2.将block
赋值给__strong
指针时。
#import <Foundation/Foundation.h>
typedef void(^KXBlock)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
int age = 10;
KXBlock blcok = ^{
NSLog(@"----%d",age);
};
NSLog(@"%@",[blcok class]);
}
return 0;
}
3.block
作为Cocoa API
中方法名
含有UsingBlock
的方法参数
时。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSArray *array = @[];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
}
return 0;
}
4.block
作为GCD API
的方法参数
时。
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
//01
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
//02
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
}
return 0;
}
MRC
下block
属性的建议写法
1.@property (copy, nonatomic) void (^block)(void);
ARC
下block
属性的建议写法
1.@property (strong, nonatomic) void (^block)(void);
2.@property (copy, nonatomic) void (^block)(void);