现在的位置: 首页 > 综合 > 正文

Blocks和Variables

2019年09月30日 ⁄ 综合 ⁄ 共 3233字 ⁄ 字号 评论关闭

译自:Blocks and Variables

翻译不不到位之处,请指正,建议点击上面链接看苹果官网介绍。

本文描述的是Blocks和Variables之间的交互,包括内存管理部分。

1. 变量类型

在block对象的函数体中,变量可能会以5中不同的方式做处理。

你可以引用三种标准类型的变量:

。全局变量,包括静态的本地变量

。全局函数(which aren’t technically variables)

。局部变量和闭合作用域内的参数

blocks也支持另外两种类型的变量:

。At function level are__block variables. These are mutable within the block
(and the enclosing scope) and are preserved if any referencing block is copied to the heap.

const imports.

最后在函数的实现中,blocks可能会引用Objective-C的实例变量。

下面的规则适用于在block内使用的变量:

。可访问全局变量,包括静态变量

。可访问传向block的参数,就跟函数一样

。闭合词法作用域内的堆栈(非静态)变量被视作const变量。它们的值在程序内的block表达式内获取,对于嵌套的blocks,该值从最近的闭合作用域内获取

。使用 __block 存储标识符声明的闭合词法作用域内的变量是通过引用提供的,所以该变量是可变的。对该变量的任何改变都可反馈到词法作用域内,包括在同一词法作用域内定义的任何其他blocks

。block的词法作用域内定义的局部变量,同函数内的局部变量完全一样。每次对block的调用都会有该变量的一个新拷贝,These variables can in turn be used asconst
or by-reference variables in blocks enclosed within the block.

下面的例子演示了局部非静态变量的用法:

int x = 123;
 
void (^printXAndY)(int) = ^(int y) {
 
    printf("%d %d\n", x, y);
};
 
printXAndY(456); // prints: 123 456

上例中,block内可直接访问变量 x,并且 x 被视作 const 类型,如果在block内尝试改变 x 的值会报错:

int x = 123;
 
void (^printXAndY)(int) = ^(int y) {
 
    x = x + y; // error
    printf("%d %d\n", x, y);
};

为了允许一个变量能够在block内改变它,你可以使用 __block 存储类型标示符。

2. __block 存储类型

可以使用 __block 存储类型标示符指定一个重要的变量可变(也就是可读可写),__block存储同局部变量的register,auto,static存储类型相似,但是不能同它们同时使用。

__block变量在存储区中存储,该存储区共享于 变量的词法作用域 及 在变量的词法作用域中声明和创建的blocks和blocks副本中。如果在堆栈框架中声明的blocks的任一副本在该堆栈框架被销毁后依然存在,那么该__block变量的存储区依然存在(例如,blocks在排队队列中等待执行)。在同一词法作用域中的多个blocks可以同时使用一个共享变量。

作为一种优化处理,blocks存储区就和blocks本身一样会跳出堆栈。当使用Block_copy(或者在Objective-C中,向block发送copy消息)复制block后,变量被拷贝到堆中。于是随着时间的改变,block变量的地址也可能改变。

使用__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'
}

3。对象和block变量

Objective-C对象

在手动的引用计数环境,当blocks被复制时,在block中使用的局部变量会被保留(retain), 在block中使用实例变量会导致包含该实例变量的对象本身被保留(retain),如果你想为一特定的对象变量重载这一行为,可以使用__block存储类型标识符标记该变量。

如果你使用ARC,block被复制或者释放时,对象变量会被自动被retain或者release。

如果在函数的实现中使用了block,对象实例变量的内存管理规则更加精细:

。如果通过引用访问实例变量, self 将会被retain

。如果通过值访问实例变量,该变量将会被retain

下面的例子展示了上面两种不同的情况:

dispatch_async(queue, ^{
    // instanceVariable is used by reference, self is retained
    doSomethingWithObject(instanceVariable);
});
 
 
id localVariable = instanceVariable;
dispatch_async(queue, ^{
    // localVariable is used by value, localVariable is retained (not self)
    doSomethingWithObject(localVariable);
});

C++对象

通常情况下可以在block中使用C++对象。在成员函数中,是通过this指针来完成对成员变量或者成员函数的引用,并且该引用是可变的。当一个block被拷贝时需要考虑两种情况:

。If you have a __block storage class for what would have been a stack-based C++ object, then the usual copy constructor is used.

。If you use any other C++ stack-based object from within a block, it must have a const copy constructor. The C++ object is then copied using that constructor.


Blocks

当你拷贝一个block,有必要的话,在该block中引用的其他block会被拷贝。从头开始的整个树会被拷贝。如果你有block变量,并且你从该block变量中引用了一个block,那么这个被引用的block将会被拷贝。

当你拷贝一个基于堆栈的block,将会获得一个新的block。当你拷贝一个基于堆的block,这只会简单的增加该block的引用计数,并且从拷贝函数或者方法的返回值获取该block。


【上篇】
【下篇】

抱歉!评论已关闭.