本章描述了blocks和变量的相互作用,包含内存管理。
Types of Variable
在block的代码体里,变量有五个不同的地方。你可以引用3个标准变量类型,就像你在function里引用一样。
全局变量,包括局部静态变量
全局functions(不是技术上的变量)
在block作用域里的局部变量和参数
Blocks 也支持其他两种变量类型:
1. 在function层面上是__block变量,他们在block(和作用域里)是可变的,如果任意引用的block被copy到堆里,
他们会被保存。
2. const imports
最后,在一个方法的实现里,blocks可能会引用OC实例变量----看Object and Block Variables.
以下规则适用于block里使用的变量:
1. 全局变量可访问,包括作用域里的静态变量
2. 传入block的参数可访问(就像function的参数(形参))
3. 局部于作用域里的栈(非静态)变量可以像const变量一样被获取到
他们的值会在program的block表达式里被获取。在嵌套的blocks里,他们的值从最近的作用域里得到。
4. 用__block存储修饰符声明的局部变量,并以引用的方式提供,这样他们就是可变的。
对该变量的任意改变都会反映到整个作用域,包括任意其他的定义在该作用域内的blocks。这些会在The __block Storage Type. 更加详细地阐述。
5. 在block里定义的局部变量,行为完全像function的局部变量。
每次调用block都会提供一份新的变量copy,这些变量可以依次被当做const使用,或者在封闭在block里的
blocks(嵌套block?)中当引用变量使用。
以下例子解析了局部非静态变量的使用
int x = 123; |
|
void (^printXAndY)(int) = ^(int y) { |
|
printf("%d %d\n", x, y); |
}; |
|
printXAndY(456); // prints: 123 456 |
如前所述,在block里试图被x赋一个新值将会产生一个error
int x = 123; |
|
void (^printXAndY)(int) = ^(int y) { |
|
x = x + y; // error |
printf("%d %d\n", x, y); |
}; |
为了允许一个变量能够在block里改变,你使用__block存储修饰符-----参考The __block Storage Type.
The __block Storage Type
你可以通过__block存储修饰符来声明一个输入变量为可变的,即可读写。对于局部变量,__block存储类似于但互斥于register,auto和static存储类型。
__block变量寄存在 变量的作用域、所有blocks 和 在变量作用域内声明或createed的 block的 copy 的共享内存里。所以,如果 声明在该域内的 任意blocks的 copy 声明周期比该域长时(例如,在某个地方enqueued以便后续执行), 这块内存在栈销毁时将会幸存下来。在一个指定范围内的多个blocks可以同时使用一个共享的变量。
对于__block变量有两个进一步的约束:他们不能为 可变长度的数组,不能为 包含C99可变长度数组的结构体。
下面例子解析了__block变量的使用:
__block int x = 123; // x lives in block storage |
|
void (^printXAndY)(int) = ^(int y) { |
|
x = x + y; |
printf("%d %d\n", x, y); |
}; |
printXAndY(456); // prints: 579 456 |
// x is now 579 |
以下例子展示了blocks和不同类型 变量相互作用
extern NSInteger CounterGlobal; |
static NSInteger CounterStatic; |
|
{ |
NSInteger localCounter = 42; |
__block char localCharacter; |
|
void (^aBlock)(void) = ^(void) { |
++CounterGlobal; |
++CounterStatic; |
CounterGlobal = localCounter; // localCounter fixed at block creation |
localCharacter = 'a'; // sets localCharacter in enclosing scope |
}; |
|
++localCounter; // unseen by the block |
localCharacter = 'b'; |
|
aBlock(); // execute the block |
// localCharacter now 'a' |
} |
Object and Block Variables
Blocks 支持OC、C++对象和其他的blocks最为变量
Objective-C Objects
当block被拷贝时,他为block里使用的对象变量创建一个强引用。如果你在一个方法的实现里使用block:
- 如果你通过引用(by reference)访问一个实例变量,一个强引用会指向self
- 如果你通过值(by value)访问一个实例变量,一个强引用会指向该变量
以下的例子解析这两种不同情况:
dispatch_async(queue, ^{ |
// instanceVariable is used by reference, a strong reference is made to self |
doSomethingWithObject(instanceVariable); |
}); |
|
|
id localVariable = instanceVariable; |
dispatch_async(queue, ^{ |
/* |
localVariable is used by value, a strong reference is made to localVariable |
(and not to self). |
*/ |
doSomethingWithObject(localVariable); |
}); |
为特定的对象变量覆盖这种行为,你可以使用__block存储修饰符来标识他。
C++ Objects
通常情况下你可以在block里使用C++对象,在一个成员函数里,引用成员变量和函数是通过暗指的this指针,所以会呈现出可变性。如果一个block被拷贝,有两个注意事项:
- 如果你有一个__block存储的类,类里有一个基于stack的C++对象,通常使用拷贝构造函数
- 如果你在一个block里使用其他基于stack的C++对象,他必须有一个const的拷贝构造函数,C++对象会使用这个构造函数来复制的。
Blocks
当你拷贝一个block时,如果有必要的话,任何在这个block里被引用的其他blocks都会被拷贝——整棵树都可能会被拷贝(自顶向下)。如果你有block变量和你引用一个 来自于该block内的 block,那么那个block(标红的block???)将会被拷贝。