iOS中block之我一点看法

今天看到一些关于block的问题。发现自己尽管在使用的时候没有注意到,但是还是有很多问题是自己平时没有注意到但是其实很重要的。我把自己看到的那些问题记录下来,让自己多温故,也为偶尔看到这篇blog的也恰好对一些问题有点疑惑的人提一点醒吧~~

1.block截获自动变量

关于这一点非常好理解。我们知道block是带有自动变量(局域变量)的匿名函数。也就是说在使用block的时候我们可以给这个block传递参数。但是当使用这个参数的时候在block中参数究竟是多少呢?

我们来分情况说明一下

1.当参数没有__block修饰时,,在block截获的自动变量的值,就是保存该变量的瞬间的值。让我们看一段代码。

int val = 0;NSLog(@"the address of val is %d", &val);void (^blk)(void) = ^{NSLog(@"the val is %d and the address is %d", val, &val);};blk();val = 1;blk();打印出的结果为:

the address of val is 1548822496

the val is 0 and the address is -2006868880

the val is 0 and the address is -2006868880

由此可见,在block截获了自动变量后,无论在外界这个自动变量如何变化,在block中都是不变的。并且通过查看地址我们可以发现在block中的val和外界声明的val根本就是两片内存。

2.当参数有__block修饰时,block可以修改该参数的值。让我们再看一段代码。

__block int val = 0;NSLog(@"the val is %d and address is %d", val, &val);//有__block修饰,则使用同一片内存void (^blk)(void) = ^{NSLog(@"the val in block is %d and address is %d", val, &val);val = 1;};blk();NSLog(@"the first val is %d", val);val = 4;blk();

打印出的结果如下:

the val is 0 and address is 1373611992

the val in block is 0 and address is 1666570600

the first val is 1

the val in block is 4 and address is 1666570600

由此可见,如果使用__block修饰声明的变量,则在block中的变量和外部声明的变量为同一片内存。一改则全改。

2.Block的实现

通过观察block结构体的isa指针我们可以发现其实block是Objective-C的一个对象。它的类为NSConcreteStackBlock等。

我们这一节主要讲述关于截取自动变量和获得由__block修饰的变量的差别。

1.截取到得自动变量会被以成员变量的形式放入block结构体中,因为是复制的一片新内存,加上没有修改的方法,所以不会被改变值了。

2.关于__block修饰的变量比较特殊,它会被生成一个__Block_byref_…类型的结构体被单独地保存在内存之中。从而不同的block可以访问到同一个值。而block中也保存*forwarding指针指向自己,然后再找到这个值,对这个值进行改变等。因为__block_byref结构体中存着变量的指针,所以我们不必担心两个值所处的内存不同。

block根据存在的内存的区域不同可分为三种:在栈中存放的是NSConcreteStackBlock,在堆中存放的为NSConcreteMallocBlock,在数据区中存放的为NSConcreteGlobalBlock.Block若要在其所在的作用域外发挥作用,则需要从栈中复制到堆中才可以。一般地,由函数返回的block都会被存放在堆中。并且在系统的大部分API如果存在usingBlock或者CGD方法中的block都会放入堆中。而block作为参数存在时,我们需要注意下是否需要手动地将其复制。复制的方法很简单,调用copy方法即可。在ARC下,多次调用copy并不会导致内存泄露。

而在block被复制时,其所使用的__block变量也会被影响。不管block之前是存放在栈中或者已经存放于堆中,当被拷贝时,其所持有的__block变量都会被放入堆中并被该block所持有。当多个block使用同一个__block变量时,当其中的一个block被复制到堆中时,__block变量也会被复制到堆中一份,并且被已经被复制到堆中的block所持有。

让我们看看下面这段代码,当一个block被从栈复制到堆上时发生了什么

__block int val = 0;NSLog(@"the val address is %d", &val);void (^blk)(void) = [^{++val;} copy];NSLog(@"the val address is %d", &val);++val;blk();NSLog(@"the val address is %d", &val);NSLog(@"the val is %d", val);控制台中打出的结果为:

the val address is 1444706264

the val address is 961698216

the val address is 961698216

the val is 2

花擦!看到一个奇怪的现象了嘛?第一个log和第二个log里,同一个变量竟然地址都是不一样的?!

其实,在访问一个__block变量时我们是通过forwarding指针访问的。而在这个block被复制到了堆之后,栈中的block的forwarding指针也会指向被复制到堆中的那份__block变量的内存。所以我们就看到了这个现象,这也是苹果保证在block使用过程中始终访问到同一个__block变量的方法。

3.block截获对象

如果block截获了一个对象,如果这个对象被strong所修饰,则在block被复制到堆上时,该对象会被持有。所以可以在该对象的作用域外被操作。

如果block截获的对象被weak所修饰,则不会被持有。

学会宽容,要有一颗宽容的爱心!

iOS中block之我一点看法

相关文章:

你感兴趣的文章:

标签云: