iOS block解析

Block简介
Block 对象是 C 级别的语法和运行时特性。它们和标准 C 函数很类似,但是除了可执行代码外,它们还可能包含了变量自动绑定(栈)或内存托管(堆)。所以一个 block 维护一个状态集(数据),它们可以在执行的时候用来影响程序行为。
blocks 来编写函数表达式,这些表达式可以作为 API 使用,或可选的存储,或被多个线程使用。Blocks 作为回调特别有用,
Blocks 通常代表一个很小、自包的代码片段。因此它们作为封装的工作单元在并 发执行,或在一个集合项上,或当其他操作完成时的回调的时候非常实用
Block 对象提供了一个使用 C 语言和 C 派生语言(如 Objective-C 和 C++)来创建 表达式作为一个特别(ad hoc)的函数。在其他语言和环境中,一个block对象有时候 被成为“闭包(closure)”。在这里,它们通常被口语化为”块(blocks)”,
一个 block 就是一个匿名的内联代码集合体:
和函数一样拥有参数类型
有推断和声明的返回类型
可以捕获它的声明所在相同作用域的状态,可以和其他定义在相同作用域范围的 blocks 进行共享更改
在相同作用域范围(栈帧)被销毁后持续共享和更改相同作用域范围(栈帧)的状态
Block创建和声明
声明和使用一个block:
使用^操作符来来声明一个 block 变量和指示 block 文本的开始。Block 本身的主体被{}包含着。
注意 block 可以使用相同作用域范围内定义的变量。
如果你声明一个 block 作为变量,你可以把它简单的作为一个函数使用
当你在多个地方使用同一个给定的签名的 block 时,这通常被认为是最佳的办法。

Typedef float (^MyBlockType)(float, float);
MyBlockType     myFirstBlock= ···
MyBlockType          mySecondBlock=···

Blocks 还支持可变参数(…)。一个没有使用任何参数的 block 必须在参数列表 上面用 void 标明。
你可以把一个 block 引用强制转换为任意类型 的指针,反之亦然。但是你不能通过修饰符 * 来解引用一个 block,因此一个 block 的大小是无法在编译的时候计算的。
直接使用一个block
在很多情况下,你不需要声明一个 block 变量;相反你可以简单的写一个内联 (inline)的 block 文本,它需要作为一个参数使用。
在 Cocoa frameworks 里面有部分方法使用 block 作为参数,通常不是执行一个对 象的集合操作,就是在操作完成的时候作为回调使用。
__block变量
Blocks 的最大一个特色就是可以修改相同作用域的变量。你可以使用__block 存 储类型修饰符来给出信号要修改一个变量。
__block 变量保存在变量共享的作用域范围内,所有的 blocks 和 block 副本都声明 或创建在和变量的作用域相同范围内。所以,如果任何 blocks 副本声明在栈内并未超 出栈的结束时,该存储会让栈帧免于被破坏(比如封装为以后执行)。同一作用域范 围内给定的多个 block 可以同时使用一个共享变量。
作为一种优化,block存储在栈上面,就像blocks本身一样。如果使用Block_copy 拷贝了 block 的一个副本(或者在 Objective-C 里面给 block 发送了一条 copy 消息), 变量会被拷贝到堆上面。所以一个__block 变量的地址可以随时间推移而被更改。
使用__block 的变量有两个限制:它们不能是可变长的数组,并且它们不能是包 含有 C99 可变长度的数组变量的数据结构。

Block和变量
你可以引用三种标准类型的变量,就像你在函数里面引用那样:
全局变量,包括静态局部变量。
全局函数(在技术上而言这不是变量)。
封闭范围内的局部变量和参数。

Blocks 同样支持其他两种类型的变量:
在函数级别是__block变量。这些在block里面是可变的(和封闭范围),并任何引 用 block 的都被保存一份副本到堆里面。
引入const。
在 block 里面使用变量遵循以下规则:
全局变量可访问,包括在相同作用域范围内的静态变量。
传递给block的参数可访问(和函数的参数一样)。
程序里面属于同一作用域范围的堆(非静态的)变量作为const变量(即只读)。
它们的值在程序里面的 block 表达式内使用。在嵌套 block 里面,该值在最近的
封闭范围内被捕获。
属于同一作用域范围内并被__block存储修饰符标识的变量作为引用传递因此是可变的。
属于同一作用域范围内block的变量,就和函数的局部变量操作一样。
每次调用 block 都提供了变量的一个拷贝。这些变量可以作为 const 来使用,或在 block 封闭范围内作为引用变量。
对象(Object)和Block变量

在引用计数的环境里面,默认情况下当你在 block 里面引用一个 Objective-C 对象的时 候,该对象会被 retain。当你简单的引用了一个对象的实例变量时,它同样被 retain。 但是被__block 存储类型修饰符标记的对象变量不会被 retain.
如果你在实现方法的时候使用了 block,对象的内存管理规则更微妙: 如果你通过引用来访问一个实例变量,self 会被 retain。如果你通过值来访问一个实例变量,那么变量会被 retain。
当你拷贝一个 block 时,任何在该 block 里面对其他 blocks 的引用都会在需要的 时候被拷贝,即拷贝整个目录树(从顶部开始)。如果你有 block 变量并在该 block 里 面引用其他的 block,那么那个其他的 block 会被拷贝一份。
当你拷贝一个基于栈的 block 时,你会获得一个新的 block。但是如果你拷贝一个 基于堆的 block,你只是简单的递增了该 block 的引用数,并把原始的 block 作为函数 或方法的返回值。
Block总结
除了前面提到的block可以作为一个函数被调用,可以作为函数的参数,可以作为方法的参数。_block还可以修饰变量,用于在快中可以对变量操作。下面block还有几种用法和注意点。
拷贝Block
通常,你不需要 copy(或 retain)一个 block.在你希望 block 在它被声明的作用域 被销毁后继续使用的话,你子需要做一份拷贝。拷贝会把 block 移到堆里面。使用 Objective-C,你可以给一个 block 发送 copy、retain 和 release(或 autorelease)消息。 保证copy和release的平衡。
自己练习的小demon

#import "BlockViewController.h"

@interface BlockViewController ()

@end

@implementation BlockViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    //block作为函数调用
    int (^square)(int) = ^(int a){return a*a;};
    int result = square(3);
    NSLog(@"%d",result);
    //block作为函数参数
    //[self myFc];

    //block作为方法参数
    [self objcMethod:square];

    //变量存取
    [self para];

    //_block修饰变量
    [self blockM];
}

-(void)objcMethod:(int(^)(int))square{
    if (square(3)>=8) {
        NSLog(@"good");
    }else NSLog(@"bad");
}


-(void)para{
    //局部变量
    int outA = 8;
    int (^myPtr)(int) = ^(int a){ return outA + a;}; //block里面可以读取同一类型的outA的值

    outA = 5;  //在调用myPtr之前改变outA的值
    int result = myPtr(3);  // result的值仍然是11,并不是8
    NSLog(@"result=%d", result);

    //静态变量
    static int outB = 8;
    int (^myPtr1)(int) = ^(int a){return outB + a;};
    outB = 5;
    int result1 = myPtr1(3);  //result的值是8,因为outC是static类型的变量
    NSLog(@"result1=%d", result1);

    //在block修改变量值
    static int outC = 8;
    int (^myPtr2)(int) = ^(int a){ outC = 5; return outC + a;};
    int result2 = myPtr2(3);  //result的值是8,因为outC是static类型的变量
    NSLog(@"result2=%d", result2);


}

-(void)blockM{
    __block int num = 5;

    int (^myPtr)(int) = ^(int a){return num++;};
    int (^myPtr2)(int) = ^(int a){return num++;};
    int result = myPtr(0);   //result的值为5,num的值为6
    int result1 = myPtr2(0);      //result的值为6,num的值为7
    NSLog(@"result=%d result1=%d", result,result1);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end

扩展:block 作为类的成员变量

可以用weak,不会将block从本身的栈区拷贝到堆区,而是让block继续在堆区自动管理。
也可以用copy,但不能用strong, block如果用到了self,就会retain self ,如果是strong的话,就造成了循环引用。 使用retain也可以,但是block的retain行为默认是用copy的行为实现的,因为block变量默认是声明为栈变量的,为了能够在block的声明域外使用,所以要把block拷贝(copy)到堆,所以说为了block属性声明和实际的操作一致,最好声明为copy。

http://blog.sina.com.cn/s/blog_134b6ff380102v1y1.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值