前言
对于大多数iOS开发人员来说,Block应该并不陌生,iOS4.0已开始支持Block,在编程过程中,Block被Objective-C看成是对象,它封装了一段代码,这段代码可以在任何时候执行。Block可以作为函数参数或者函数的返回值,而其本身又可以带输入参数或返回值。它是对C语言的扩展,用来实现匿名函数的特性。
Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值。
Block的特殊特性方便了开发人员的使用,但是Block的内存需要开发人员自己管理,错误的内存管理会造成循环引用内存泄露,或者内存因为提前释放造成崩溃。
因此,Block的内存管理是很重要的,本文将主要讨论Block使用过程中的内存管理问题。
开发环境
- Xcode 9正式版
- ARC
- 真机调试 iPhone 7 Plus 11.0.1
- MacBook Pro 10.13
创建代码如下:
//局部变量
- (void)localVar
{
int localVar = 10;
NSLog(@"localVar --%p",&localVar);
sumBlock block1 = ^(int x,int y){
NSLog(@"localVar --%p",&localVar);
return localVar + x + y;
};
localVar = 0;
int z = block1(1,2);
NSLog(@"localVar -- z = %d",z);
}
打印结果:
//静态变量
- (void)staticVar
{
static int staticVar = 10;
NSLog(@"staticVar --%p",&staticVar);
sumBlock block2 = ^(int x,int y){
NSLog(@"staticVar --%p",&staticVar);
return staticVar + x + y;
};
staticVar = 0;
int z = block2(1,2);
NSLog(@"staticVar -- z = %d",z);
}
打印结果:
//全局变量
- (void)globalVar
{
NSLog(@"globalVar --%p", &globalVar);
sumBlock block3 = ^(int x, int y) {
NSLog(@"globalVar --%p", &globalVar);
return globalVar + x + y;
};
globalVar = 0;
int z = block3(1, 2);
NSLog(@"globalVar -- z = %d", z);
}
打印结果:
//__block
- (void)blockVar
{
__block int blockVar = 10;
NSLog(@"blockVar --%p", &blockVar);
sumBlock block4 = ^(int x, int y) {
NSLog(@"blockVar --%p", &blockVar);
return blockVar + x + y;
};
blockVar = 0;
int z = block4(1, 2);
NSLog(@"blockVar -- z - %d", z);
}
打印结果:
总结
- block中的变量是静态或者全局类型时,在block中该变量的内存地址没有发生改变。由于静态变量和全局变量其地址是固定的,因此block在定义的时候并没有复制该变量的值,而是直接从其所在内存中读出。
- block中的变量是局部类型时,在block中它的地址都发生了变化,block在定义的时候拷贝了它(开辟了新的内存空间),它在block中是作为常量使用的,其值不受外面的影响。
- block中的变量是__block类型时,在block中它的地址都发生了变化,但是blockVar的值受到外界影响,
这是因为blockVar在定义变量本身的时候是位于stack上的,而在定义block的时候,该变量并不是被复制了一份,而是编译器将其转移到了heap上,这个地方其实变量是被复制了一份的,并且作为生成的新的结构体的一个成员变量。这个是更深层次的东西,等研究关于__block的原理的东西的时候会有相关介绍。