Block-栈,堆,全局块

栈块、堆块、全局块 (Block详解)

对于Block之前只是在用,对于栈,堆这块没有细入研究,今天抽空把”Effectiv Objective-c 2.0”这本书看了一下,下面是一些概念及实例。

分几类

  • 栈Block
  • 堆Block
  • 全局Block
    block的存储形态有三种:_NSConcretStackBlock(栈)、_NSConcretGlobalBlock(全局)、_NSConcretMallocBlock(堆)

要点一:当block在函数内部,且定义的时候就使用了函数内部的变量,那么这个 block是存储在栈上的。

要点二:当block定义在函数体外面,或者定义在函数体内部且当时函数执行的时候,block体中并没有需要使用函数内部的局部变量时,也就是block在函数执行的时候只是静静地待在一边定义了一下而不使用函数体的内容,那么block将会被编译器存储为全局block。

要点三:全局block存储在堆中,对全局block使用copy操作会返回原函数指针;而对栈中的block使用copy操作,会产生两个不同的block地址,也就是两个匿名函数的入口地址。

栈Block

直接声明使用的block是分配在栈内,为栈block,只会在定义它的范围内有效。

注意:下面这种写法MRC环境下就有问题。

-(void)doSomething{
    void (^block)();
    if(/* some condition */){
        block = ^{
            NSLog(@"Block A");
        };
    }else{
        block = ^{
            NSLog(@"Block B");
        };
    }
    block();
}

定义在if及else语句中的两个block都分配在栈内存中,编译器会给每个块分配好栈内存,等离开了相应的范围之后,编译器有可能把分配给块的内存覆写掉,这两个块只能保证在对应的if或else语句范围内有效。能正常编译,但运行起来时而正确,时而错误,若编译器未覆写待执行的块,则程序运行正常,若覆写,则和序崩溃。

对于此类问题,可给块对象发送copy消息,拷贝。这样就可以把block从栈复制到堆,拷贝到堆中后, block就成了带引用计数的对象了,后续的复制只会递增对象的引用计数,如果不再使用这个块,就需要释放,ARC下系统统会自动释放,当引用计数降为0后,与其它对象一致,为系统回收。

而分配在栈上的block,无须明确释放,因为栈内存本来就会自动回收,上面那段代码之所以有风险,就是来源于此。

介与此,MRC环境下,上面那段代码如此修改就没有问题了。

-(void)doSomething{
    void (^block)();
    if(/* some condition */){
        block = [^{
            NSLog(@"Block A");
        } copy];
    }else{
        block = [^{
            NSLog(@"Block B");
        } copy];
    }
    block();
}

除了”栈块”和”堆块”之外,还有一类块叫做”全局块”,这种块不会捕捉任何状态,运行时也无须有状态来参与,块所使用的整个内存区域,在编译期已经完全确定了,因此,全局块可以声明在全局内存里,而不需要在每次用至的时候于栈中创建。别外全局块的拷贝操作是个空操作,因为全局块决不可能为系统所回收,这种块实际相当于单例。下面就是个全局块:

    void (^block)()=^{
        NSLog(@"This is a block");
    }
展开阅读全文

没有更多推荐了,返回首页