8OC使用原理 -8-block-本质,类型,copy属性详解
2020-01-20 本文已影响0人
zysmoon
1.Block的本质
- block本质上也是一个OC对象,它内部也有个isa指针
- block是封装了函数调用以及函数调用环境的OC对象
- block的底层结构如下图所示
代码佐证
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// block底层结构体
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age;
};
// 1.block结构体
void test1() {
int age = 10;
// 定义block
void(^block)(int, int) = ^(int a, int b){
NSLog(@"this is a block! -- %d", age);
NSLog(@"a = %d, b = %d",a,b);
};
struct __main_block_impl_0 *blockStruct = (__bridge struct __main_block_impl_0 *)block;
block(100,200);
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 1\. block结构体
test1();
}
return 0;
}
1653926-164d8fe25b3a121f.png
2.block的变量捕获(capture)
- 为了保证block内部能够正常访问外部的变量,block有个变量捕获机制
-
auto
:自动变量,离开作用域就销毁
代码例子如下:
// 捕获变量类型
void (^block)(void);
void blockTest() {
int age = 10;
static int height = 10;
block = ^ {
NSLog(@"age is %d, height is %d", age, height);
};
age = 20;
height = 20;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
blockTest();
block();
}
return 0;
}
运行结果
1653926-5c061022aba45eb2.png3.block的类型
- block有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型
__NSGlobalBlock__ (_NSConcreteGlobalBlock )
__NSStackBlock__ ( _NSConcreteStackBlock )
__NSMallocBlock__ ( _NSConcreteMallocBlock )
每一种类型的block调用copy后的结果如下所示
代码例子如下
先将环境切换为MRC,setting -> OC Automitic RF -> NO
// 4.block的类型
int weight = 100;
void (^block)(void);
void blockClassType() {
// 堆:动态分配内存,需要程序员申请申请,也需要程序员自己管理内存
static int age = 10;
// 局部static变量
int height = 10;
// Global:没有访问auto变量
void(^block1)(void) = ^ {
NSLog(@"block1");
};
// Globa2:访问static变量
void(^block2)(void) = ^ {
NSLog(@"block2 - age = %d",age);
};
// Globa3:访问全局变量
void(^block3)(void) = ^ {
NSLog(@"block3 - weight = %d",weight);
};
// Stack:访问了auto变量
void (^block4)(void) = ^{
NSLog(@"block4 - height = %d", height);
};
// NSMallocBlock - 对StackBlock做copy操作
block = [^{
NSLog(@"block---------%d", height);
} copy];
[block release];
NSLog(@"%@ %@ %@ %@ %@",
[block1 class],
[block2 class],
[block3 class],
[block4 class],
[block class]
);
}
运行结果如下
1653926-21fb125822f77ec6.png4. 数据存储位置
代码例子如下
// 5.数据存储位置
int age = 100;
void dataLocationTest() {
int a = 10;
NSLog(@"数据段:age %p", &age);
NSLog(@"栈:a %p", &a);
NSLog(@"堆:obj %p", [[NSObject alloc] init]);
NSLog(@"数据段:class %p", [Person class]);
}
打印结果
1653926-1e441e8b323b6c1c.png5.block的copy
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况
- block作为函数返回值时
- 将block赋值给__strong指针时
- block作为Cocoa API中方法名含有usingBlock的方法参数时
- block作为GCD API的方法参数时
代码例子如下
1.block作为函数返回值时
// 定义一个block
typedef void(^CSBlock)(void);
// 定义一个返回blcok的函数
CSBlock myBlock() {
int age = 10;
return ^{
NSLog(@"age = %d",age);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
// 1.block作为函数返回值
CSBlock block = myBlock();
block();
NSLog(@"%@",[block class]);
}
return 0;
}
运行结果
1653926-dd660159f77dff26.png2.将block赋值给__strong指针时
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 2.block有被强指针引用
// ARC - 环境下 block - __NSMallocBlock__
// MRC - 环境下 block - __NSStackBlock__
int age = 10;
CSBlock block = ^{
NSLog(@"---------%d", age);
};
NSLog(@"%@", [block class]);
}
return 0;
}
打印结果
1653926-76c4fbacd99bd6b1.png 1653926-3459356b2391d606.png3.block作为Cocoa API中方法名含有usingBlock的方法参数时
// 3.block作为Cocoa API中方法名含有usingBlock的方法参数时
NSArray *array = [NSArray array];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
}];
4.block作为GCD API的方法参数时
// 4.block作为GCD API的方法参数时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
block建议写法
-
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
-
ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
本文参考:
路飞_Luck (https://www.jianshu.com/p/07f7b96bb03f)
以及借鉴MJ的教程视频
非常感谢.
项目演示代码如下
iOS_block_本质
iOS-block-copy