[OC学习笔记]Block三种类型
Posted Billy Miracle
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[OC学习笔记]Block三种类型相关的知识,希望对你有一定的参考价值。
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
)。
以上是关于[OC学习笔记]Block三种类型的主要内容,如果未能解决你的问题,请参考以下文章