zoukankan      html  css  js  c++  java
  • block存储区域——怎样验证block在栈上,还是堆上

    Block存储区域

    首先,须要引入三个名词:
    ● _NSConcretStackBlock
    ● _NSConcretGlobalBlock
    ● _NSConcretMallocBlock
    正如它们名字显示得一样,表明了block的三种存储方式:栈、全局、堆。block对象中的isa的值就是上面当中一个,以下開始说明哪种block存储在栈、堆、全局。block为何是个对象,參考点击打开链接

    ------------【要点1】:全局block------------

    ● 定义在函数外面的block是global类型的
    ● 定义在函数内部的block,可是没有捕获不论什么自己主动变量,那么它也是全局的。比方以下这种代码
    typedef int (^blk_t)(int);
    for(...){
        blk_t blk = ^(int count) {return count;};
    }
    尽管,这个block在循环内,可是blk的地址总是不变的。说明这个block在全局段。注:针对没有捕获自己主动变量的block来说,尽管用clang的rewrite-objc转化后的代码中仍显示_NSConcretStackBlock,可是实际上不是这种。下图能够证明该类型的block是全局的。Xcode5.1.1调试结果

    不管ARC与否上图控制台输出是 <__NSGlobalBlock__: 0x10000f280>,这可能是编译器的优化,本人猜測,没有求证。所以用clang的-rewrite-objc是不准确的。

    ------------【要点2】:栈block--------------

    这样的情况,在非ARC下是无法编译的,在ARC下能够编译
    typedef void (^block_t)() ;
    -(block_t)returnBlock{
        __block int add=10;
        return ^{printf("add=%d
    ",++add);};
    }
    这是由于:block捕获了栈上的add自己主动变量,此时add已经变成了一个结构体,而block中拥有这个结构体的指针。即假设返回block的话就是返回局部变量的指针。而这一点恰是编译器已经断定了。在ARC下能够编译过,是由于ARC使用了autorelease了。
    再说一个场景:

    -(block_t)returnBlock{
        __block int add=10;
        block_t blk_h =^{printf("add=%d
    ",++add);};
        return blk_h;
    }
    block_t bb = [self returnBlock];
    bb();

    这段代码,仅仅是使用了一个自己主动block变量,能够编过,可是造成程序崩溃了。
    假设在返回block的时候加上copy,能够输出正确的数值11

    ------------【要点3】:堆上的block ----------------

    有时候我们须要调用block 的copy函数,将block复制到堆上。看以下的代码:
    -(id) getBlockArray{
        int val =10;
        return [NSArray arrayWithObjects:
            ^{NSLog(@"blk0:%d",val);},
            ^{NSLog(@"blk1:%d",val);},nil];
    }
    
    id obj = getBlockArray();
    typedef void (^blk_t)(void);
    blk_t blk = (blk_t){obj objectAtIndex:0};
    blk();
    这段代码在最后一行blk()会异常,由于数组中的block是栈上的。由于val是栈上的。解决的方法就是调用copy方法。这样的场景,ARC也不会为你加入copy,由于ARC不确定,採取了保守的措施:不加入copy。所以ARC下也是会异常退出。

    ---------------------【要点4】copy的使用-----------------------------------

    无论block配置在何处,用copy方法复制都不会引起不论什么问题。
    在ARC环境下,假设不确定是否要copy block虽然copy就可以。ARC会打扫战场。
    【注意】:
    ● 在栈上调用copy那么拷贝到堆上
    ● 在全局block调用copy什么也不做
    ● 在堆上调用block 引用计数添加

    ------------------【对《Objective-C 高级编程》的挑战】-----------------------

        笔者用Xcode 5.1.1 iOS sdk 7.1 编译发现:并不是《Objective-C 高级编程》这本书中描写叙述的那样,-rewrite-objc这个命令转化的中间代码,并不可靠。
        block在ARC和非ARC有巨大区别:以下笔者用两种方式来验证:
    1.通过Xcode调试结果,附图
    2.通过变量的地址 int val肯定是在栈上的,我保存了val的地址,看看block调用前后是否变化。输出一致说明是栈上,不一致说明是堆上。
    【第一个方法】,最直观.代码例如以下,非常easy。block捕获了变量val(不管val是否是__block
    -(void) stackOrHeap{
        __block int val =10;
        blkt1 s= ^{
            return ++val;};
        s();
        blkt1 h = [s copy];
        h();
    }
    在非ARC和ARC下,调试结果例如以下:
    能够看到非ARC下一个是stack一个是Malloc。ARC下都是Malloc

    【第二个方法】,声明一个局部变量指针。通过指针来看
    typedef int (^blkt1)(void) ;
    -(void) stackOrHeap{
        __block int val =10;
        int *valPtr = &val;//使用int的指针,来检測block究竟在栈上,还是堆上
        blkt1 s= ^{
            NSLog(@"val_block = %d",++val);
            return val;};
        s();
        NSLog(@"valPointer = %d",*valPtr);
    }
    在ARC下>>>>>>>>>>>该block被会直接生成到堆上了。看log: val_block = 11 valPointer = 10
    在非ARC下>>>>>>>>>该block还是在栈上的。 看log:val_block = 11 valPointer = 11

    调用copy之后的结果呢:

    -(void) stackOrHeap{
        __block int val =10;
        int *valPtr = &val;//使用int的指针,来检測block究竟在栈上,还是堆上
        blkt1 s= ^{
            NSLog(@"val_block = %d",++val);
            return val;};
        blkt1 h = [s copy];
        h();
        NSLog(@"valPointer = %d",*valPtr);
    }

    在ARC下>>>>>>>>>>>无效果。 val_block = 11 valPointer = 10
    在非ARC下>>>>>>>>>确实拷贝到堆上了。 val_block = 11 valPointer = 10

    ----------------【总结】-----------------

    用这个表格来表示。捕获变量包含只读取变量,和__block这样的写变量,两种方式(事实上结果是一样的)

    在ARC下:似乎已经没有栈上的block了,要么是全局的,要么是堆上的
    在非ARC下:存在这栈、全局、堆这三种形式。
  • 相关阅读:
    通过串口抓取图片
    Qt也有垃圾回收(通过QScopedPointer实现),下决心在项目里使用QScopedPointer,省了太多事情了,而且更安全!!
    IOS端 margin-top 和 margin-bottom 使用负数时的区别
    使用ROME解析rss,如何获取icon图标
    SVG图片如何调整大小和颜色
    Js点击触发Css3的动画Animations、过渡Transitions效果
    如何判断是否为同一个App,Ionic3如何修改包名
    如何使用JPQL写纯SQL语句
    为何在新线程中使用注解获取不到Spring管理的Bean
    Ionic的NavController 和ModalController 的区别
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4237261.html
Copyright © 2011-2022 走看看