当前位置:网站首页>[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)。
边栏推荐
猜你喜欢

HCIP第七天

redis-advanced

Biotin-EDA|CAS:111790-37-5| 乙二胺生物素

MySQL优化之慢日志查询

Biotin-EDA|CAS:111790-37-5| Ethylenediamine biotin

Kind of weird!Access the destination URL, the host can container but not

王学岗-编译出运行的文件

IO process thread -> process -> day4
![[Unity3D] Beginner Encryption Skills (Anti-Cracking)](/img/07/4a0731dd66b058c07d6240ffd36eea.png)
[Unity3D] Beginner Encryption Skills (Anti-Cracking)

CASA模型、CENTURY模型应用与案例分析
随机推荐
科技云报道:实现元宇宙,英伟达从打造基础建设平台开始
Biotin-LC-Hydrazide|CAS:109276-34-8|生物素-LC-酰肼
R语言plotly可视化:使用plotly可视化模型预测真阳性率(True positive)TPR和假阳性率(False positive)FPR在不同阈值(threshold)下的曲线
postgres 水平分表,自动创建分区,按时间分表
Transimpedance amplifier
暂未找到具体原因但解决了的bug
52. [Bool type input any non-0 value is not 1 version reason]
传递泛型给JSX元素
如何开启mysql慢查询日志?
nodejs 简介
商业智能平台BI 商业智能分析平台 如何选择合适的商业智能平台BI
MySQL - locking mechanism
prometheus monitoring mysql_galera cluster
自定义table表格
HCIP第七天
DeadLock的可视化分析
2022-2023 十大应用开发趋势
Redisson distributed lock source code analysis for high-level use of redis
离线部署通过tiup 配置好topology.yaml文件指定PD TV TIDBserver 是不是会自动在其他机器创建好对应得模块?
2022-7-31 12点 程序爱生活 恒指底背离中,有1-2周反弹希望