当前位置:首页 > 开发教程 > 手机开发 >

【Block编程第二篇】 block捕获变量和对象

时间:2015-04-27 21:19 来源: 作者: 收藏

block连载博客共四篇。 点击查看block第一篇,block语法 点击查看block第三篇,block内存管理 点击查看,block避免循环引用。---------------------------------------------------------------------------------------------------------------------------

欢迎查看block连载博客:

【block编程第一篇】block语法

【block编程第二篇】block捕获变量和对象(当前)

【block编程第三篇】block的内存管理。

【block编程第四篇】block内部实现;

【block编程第五篇】block中如何避免循环引用

------------------------------------------------------------------------------------------------------------------------------

下面介绍,block捕获自动变量和对象

一、捕获自动变量值

首先看一个经典block面试题:
int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
val = 2;
blk();

    上面这段代码,输出值是:val = 10.而不是2. 

    block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝;换句话说block截获自动变量的瞬时值;或者block捕获的是自动变量的副本。(我更喜欢这么理解:block捕获变量的机制更像是函数按值传递。

    由于block捕获了自动变量的瞬时值,所以在执行block语法后,即使改写block中使用的自动变量的值也不会影响block执行时自动变量的值。深入了解block是如何实现捕获变量的,点击查看【block四篇】block的实现

__block说明符

    前面讲过block所在函数中的,捕获自动变量。但是不能修改它,不然就是编译错误。但是可以改变全局变量、静态变量、全局静态变量。其实这两个特点不难理解:
    ● 不能修改自动变量的值是因为:block捕获的是自动变量的副本,名字一样。如果可以赋值,就造成不对称给的效果
    ● 可以修改静态变量的值:静态变量属于类的,不是某一个变量。由于block内部不用调用self指针。所以block可以调用。
    解决block不能修改自动变量的值,这一问题的另外一个办法是使用__block修饰符。
__block int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
val = 2;
blk();
上面的代码,跟第一个代码段相比。只是多了一个__block修饰符。但是输出结果确是2了。
__block 变量的内部实现要复杂许多,__block 变量其实是一个结构体对象,拷贝的是指向该结构体对象的指针。点击这里查看【block四篇】block的实现

二、捕获OC对象

ObjC对象,不同于基本类型,Block会引起对象的引用计数变化。
@interface MyClass : NSObject {
    NSObject* _instanceObj;
}
@end

@implementation MyClass

NSObject* __globalObj = nil;

- (id) init {
    if (self = [super init]) {
        _instanceObj = [[NSObject alloc] init];
    }
    return self;
}

- (void) test {
    static NSObject* __staticObj = nil;
    __globalObj = [[NSObject alloc] init];
    __staticObj = [[NSObject alloc] init];

    NSObject* localObj = [[NSObject alloc] init];
    __block NSObject* blockObj = [[NSObject alloc] init];

    typedef void (^MyBlock)(void) ;
    MyBlock aBlock = ^{
        NSLog(@"%@", __globalObj);
        NSLog(@"%@", __staticObj);
        NSLog(@"%@", _instanceObj);
        NSLog(@"%@", localObj);
        NSLog(@"%@", blockObj);
    };
    aBlock = [[aBlock copy] autorelease];
    aBlock();

    NSLog(@"%d", [__globalObj retainCount]);
    NSLog(@"%d", [__staticObj retainCount]);
    NSLog(@"%d", [_instanceObj retainCount]);
    NSLog(@"%d", [localObj retainCount]);
    NSLog(@"%d", [blockObj retainCount]);
}
@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        MyClass* obj = [[[MyClass alloc] init] autorelease];
        [obj test];
        return 0;
    }
}
执行结果为1 1 1 2 1。
__globalObj和__staticObj在内存中的位置是确定的,所以Block copy时不会retain对象。
_instanceObj在Block copy时也没有直接retain _instanceObj对象本身,但会retain self。所以在Block中可以直接读写_instanceObj变量。
localObj在Block copy时,系统自动retain对象,增加其引用计数。
blockObj在Block copy时也不会retain。点击这里查看【block第五篇】block中如何避免循环引用

安卓开发复习笔记——Fragment+FragmentTab

2014年最新720多套Android源码2.0GB免费一

开源直播系统源码功能一览表(含ios+androi

直播系统开发中低延迟优化方面的部分技巧

Android Hook 机制之简单实战

直播平台开发中美颜、滤镜的技术要求

直播源码的崛起的巅峰:布谷一对一视频直播

直播系统开发中视频采集的技术分析

手机直播系统开发的实时网络通信技术的要求

短视频平台开发中视频编码如何解决延迟优化

值得安卓开发者收藏的Android 开源项目分类

怎样保证直播平台开发完成后程序的稳定性?

手机开发阅读排行

最新文章