当前位置:网站首页>[OC学习笔记]Block三种类型
[OC学习笔记]Block三种类型
2022-08-02 07:51:00 【Billy Miracle】
Block的三种类型
block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。
使用如下代码:
void testBlockType(void) {
int age = 10;
void(^block1)(void) = ^{
NSLog(@"block1----");
};
void(^block2)(void) = ^{
NSLog(@"block2----%d", age);
};
NSLog(@"block1-----%@ %@ %@ %@", [block1 class], [[block1 class] superclass], [[[block1 class] superclass] superclass], [[[[block1 class] superclass] superclass] superclass]);
NSLog(@"block2-----%@ %@ %@ %@", [block2 class], [[block2 class] superclass], [[[block2 class] superclass] superclass], [[[[block2 class] superclass] superclass] superclass]);
NSLog(@"block-----%@ %@ %@ %@", [^{
NSLog(@"block----%d", age);
} class], [[^{
NSLog(@"block----%d", age);
} class] superclass], [[[^{
NSLog(@"block----%d", age);
} class] superclass] superclass], [[[[^{
NSLog(@"block----%d", age);
} class] superclass] superclass] superclass]);
}
打印结果:
三个block的类型分别为:__NSGlobalBlock__、__NSMallocBlock__、__NSStackBlock__上述三种类型最终都是继承自NSBlock,而NSBlock又是继承自NSObject:此处又进一步说明block其实就是一个OC对象。
- NSMallocBlock(_NSConcreteMallocBlock) 对象存储在堆区
- NSStackBlock(_NSConcreteStackBlock) 对象存储在栈区
- NSGlobalBlock(_NSConcreteGlobalBlock)对象存储在数据区
换到MRC环境下试一下:
MRC模式下,三种block类型:__NSGlobalBlock__、__NSStackBlock__、__NSStackBlock__,为什么中间的类型由malloc变成了stack?这是因为ARC自动帮助我们对block进行了copy操作。
分析
我们先尝试转化C++代码查看类型:
struct __testBlockType_block_impl_0 {
struct __block_impl impl;
struct __testBlockType_block_desc_0* Desc;
__testBlockType_block_impl_0(void *fp, struct __testBlockType_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __testBlockType_block_impl_1 {
struct __block_impl impl;
struct __testBlockType_block_desc_1* Desc;
int age;
__testBlockType_block_impl_1(void *fp, struct __testBlockType_block_desc_1 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __testBlockType_block_impl_2 {
struct __block_impl impl;
struct __testBlockType_block_desc_2* Desc;
int age;
__testBlockType_block_impl_2(void *fp, struct __testBlockType_block_desc_2 *desc, int _age, int flags=0) : age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
发现都是_NSConcreteStackBlock类型。不能正确反应block的实质类型(可能是LLVM编译器版本的问题,而clang又是LLVM的一部分)。
程序分区除了堆区需要程序员手动管理内存外,其他区都由系统自动管理。
{
//等号左边:auto形局部变量,存放在栈区;
//等号右边:常量,存放在数据区;
int age = 10;
//等号左边:auto形局部变量(指针),存放在栈区;
//等号右边:auto形局部变量地址,存放在栈区;
int *agePtr = &age;
//等号左边:auto形局部变量(指针),存放在栈区;
//等号右边:alloc开辟的对象,存放在堆区;
NSObject *objc = [[NSObject alloc] init];
}
三种block类型(global、malloc、stack),从字面理解,可以推断依次存放在数据区、堆区、栈区;blcok1没有访问任何变量,后两个block都访问量变量age,而age是一个auto类型的局部变量。
int height = 150;
void testBlockType(void) {
int age = 10;
static int weight = 80;
void(^block1)(void) = ^{
NSLog(@"block1----");
};
void(^block2)(void) = ^{
NSLog(@"block2----%d", age);
};
void(^block3)(void) = ^{
NSLog(@"-----%d", weight);
};
void(^block4)(void) = ^{
NSLog(@"-----%d", height);
};
NSLog(@"%@ %@", [block3 class], [block4 class]);
}
结果:
如果是static修饰的局部变量,或者访问全局变量,则block的类型都是__NSGlobalBlock__,那么基本上可以肯定,block的类型可以取决于其访问的变量的属性。
因为auto类型的局部变量是存放在栈区的,而block要访问该变量,经前面分析,block会将该变量捕获到block结构体内部,即重新开辟内存来存放该局部变量(相当于copy操作,但不是copy),那么此时的block自己是存放在哪个区呢?前面说了,auto类型的局部变量一定是存放在栈区的,这点毋庸置疑,而block虽然新开辟内存来存放该变量,但改变不了该变量是一个auto类型的局部变量的属性,因此此时的block也只能存放在栈区;既然存放在栈区,则访问的变量作用域仅限于离其最近的大括号范围内,超出则被自动释放。
MRC下测试下面代码:
void(^block4)(void);
void test3()
{
int age = 10;
block4 = ^{
NSLog(@"----%d", age);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test3();
block4();
// NSLog(@"%@", [block4 class]);
}
return 0;
}
输出:
说明age已经被自动释放,block再次调用时,访问的是被废弃的内存。手动copy:
void(^block4)(void);
void test3()
{
int age = 10;
block4 = [^{
NSLog(@"----%d", age);
} copy];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test3();
block4();
NSLog(@"%@", [block4 class]);
}
return 0;
}
结果:
因为copy是把age的值直接拷贝到了一块新的内存区域,而我们知道copy操作开辟的内存必定是在堆区(同时,block的类型由之前的__NSStackBlock__类型变为__NSMallocBlock__类型)。因此,防止一个auto类型的局部变量自动释放的方法,就是将其copy到堆区进行手动管理,达到对其生命周期可控的目的(所以记得要释放[block release])——这是MRC模式下的手动管理内存,而在ARC模式下系统会自动管理内存(copy和release)。
边栏推荐
猜你喜欢
随机推荐
ROS文件系统以及相关命令
小说里的编程 【连载之二十一】元宇宙里月亮弯弯
MySQL优化:从十几秒优化到三百毫秒
2022-2023 十大应用开发趋势
[Unity3D] Beginner Encryption Skills (Anti-Cracking)
ROS file system and related commands
metabase访问adb mysql 如何控制会话时区??
HCIP9_BGP增加实验
cas:139504-50-0 美登素DM1|Mertansine|
7.联合索引(最左前缀原则)
AcWing 2811. 最长公共子串(后缀自动机 fa 指针的性质)
.NET静态代码织入——肉夹馍(Rougamo) 发布1.1.0
WebForm DropDownList分别绑定年月
Control 'ContentPlaceHolder1_ddlDepartment' of type 'DropDownList' must be placed inside a form tag with runat=server.
Database Plus 的云上之旅:SphereEx 正式开源 ShardingSphere on Cloud 解决方案
Biotin-C6-amine|N-biotinyl-1,6-hexanediamine|CAS: 65953-56-2
@FeignClient configuration参数配置
BGP通过MPLS解决路由黑洞
OSPF 综合实验
HCIP 第九天









