聊聊OC 中的Block

1.blcok的概念:

闭包是一个函数或指向函数的指针 + 该函数执行的外部的上下文变量; block 实际上就是 Objective-C 语言对于闭包的实现,在调用时才执行block中代码.

2.block 的底层解析

见博文http://www.jianshu.com/p/51d04b7639f1

总而言之, block就是一个里面存储了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体。
block底层结构里包含了isa指针(包含isa指针的皆为对象),所以block也是一个对象,(runtime里面,对象和类都是用结构体表示)

3.block 的类型

block的常见类型有3种:

  • _NSConcreteGlobalBlock (全局) : 静态block,不会访问任何外部变量。
  • _NSConcreteStackBlock (栈) : 当函数返回时会被销毁
  • _NSConcreteMallocBlock (堆) : 当引用计数为0时会被销毁.
怎么判断一个block所在的存储位置呢?

block不访问外界变量(包括栈中和堆中的变量)
block既不在栈中也不在堆中,此时就为全局块,ARC和MRC下都是如此。

block访问外界变量
MRC环境下:访问外界变量的block默认存储在栈区。

ARC环境下:访问外界变量的block默认存放在堆中,实际上是先放在栈区,在ARC情况下自动又拷贝到堆区,自动释放。

NSConcreteGlobalBlock和NSConcreteStackBlock

// 全局blcok
void (^globalBlock)() = ^{

};       

int main(int argc, const char * argv[]) {
    @autoreleasepool {

        //栈上block
        void (^stackBlock1)() = ^{

        };
    }
    return 0;
}

底层结构中全局block的isa指向_NSConcreteGlobalBlock,即在全局区域创建,编译时具体的代码就已经确定在上图中的代码段中了,block变量存储在全局数据存储区;
而栈上block的isa指向了_NSConcreteStackBlock,即在栈区创建.

_NSConcreteStackBlock

堆中的block无法直接创建,其需要由_NSConcreteStackBlock类型的block拷贝而来(也就是说block需要执行copy操作后才能存放到堆中).

4.捕捉变量对block结构的影响

  1. 局部变量
- (void)test
{
    int a;
    ^{a;};
}

block结构体中增加了一个int类型的变量,用来存储外部变量a,这次拷贝是一次值传递,并且要在block中对其进行操作将会发生错误:

^{a = 10;};     //block中不能修改局部变量

编译器会报错:Variable is not assignable (missing __block type specifier)

2.全局变量

// 全局静态
static int a;
// 全局
int b;
- (void)test
{

    ^{
        a = 10;
        b = 10;
    };
}

因为全局变量都是在静态数据存储区,在程序结束前不会销毁,所以block直接访问对应变量, 不进行拷贝等操作.

3.局部静态变量

- (void)test
{
    static int a;
    ^{
        a = 10;
    };
}

静态局部变量也是存储在静态数据储存区域的,也程序拥有一样的声明周期,也就是说在程序运行时,都能够保证block访问到一个有效的变量,但是其作用范围还是局限于定义它的函数中,所以需要通过静态局部变量的地址来进行访问.

4._block修饰的变量

- (void)test
{
   __block int a;
    ^{
        a = 10;
    };
}

当block被copy到堆中时,会将 a 也拷贝到堆中,所以即使局部变量所在堆被销毁,block依然能对堆中的局部变量进行操作.

总结:
1. 对于block外的变量引用,block默认是将其复制到其数据结构中来访问的 (只可读不可修改)
2. 对于用_block 修饰的外部变量引用,block是复制其引用地址来实现访问的.

思考:

-(void)test {
    int a = 10;
    __block int b = 20;
    void (^block1)() = ^() {
        NSLog(@"a=%d,b=%d",a,b);
    };
    a = 30;
    b = 40;
    block1();
}

//结果输出什么?

结果为: a=10,b=40

答:虽然我们在调用block1之前改变了a的值,但是输出的还是Block编译时候a的值,所以截获瞬间自动变量就是:在Block中会保存变量的值,而不会随变量的值的改变而改变.
而b被__block 关键字修饰, 传递的指针, b的值会跟着源变量值得变化而变化,知道调用前才确定,所以结果为40.

如果不妥当之处,欢迎各位大神指正.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值