Block 作为Objective-C 物件

前面提到,Block 其实可以当成是一种实例,我们接下来就要来看block 的作为实例的特性。

内存管理

由于Objective-C实例需要做内存管理,因此,如果你还在手动管理内存,在建立block的时候,也必须手动管理Block的内存。我们可以使用Block_Copy()Block_Release()这两个C Function对block做copy或release;或,我们也可以把block给cast成id型别,便可以对block调用copy与release。不过在启用ARC之后,我们便不需要,Compiler也会阻止我们手动管理内存。

Block 的类型

Objective-C 当中每个实例都具有class,而每个class 都继承自NSObject,由于block 具有实例的性质,因此block 本身也有class—不过,一个block 是属于哪一种class 平时对我们来说并不会有太大的意义,毕竟我们在建立block 的时候,并不会指定要建立哪一种class 的block,我们也不会去subclass 某种block 的class。基本上,当我们写好一个block 之后,这个block 最后会变成哪个class,全部都是由compiler 决定。

在C语言当中,内存分成三块:global、stack与heap,compiler在编译程式码的时候,会根据我们所写出来的block到底使用到哪一块记忆体,将这个block变成不同的class,包括__NSGlobalBlock____NSStackBlock____NSMallocBlock__。知道这件事情通常对我们不会有什么帮助,但是可以让我们了解苹果的compiler曾经发生过的bug:在某个状况下,有个block应该要使用global的内存,但是compiler却误判成只使用stack的内存。

如果没有开启ARC,以下这段程式码会在执行到block()这一行的时候,发生Bad Access错误:

- (NSArray *)blocks
{
    int i = 1;
    return @[^{return i;}];
}

- (void)callBlock
{
    int (^block)(void) = [self blocks][0];
    block();
}

原因是:在-blocks所回传的NSArray中所包含的block实例中,使用到了i这个只出现在blocks这个method内部的int变量,因为这个变量只在这个method中使用,compiler便认为i应该使用stack的记忆体,因此也把回传的block建立成__NSMallocBlock__;于是,当我们在-callBlock里头调用block()的时候,原本的内存已经被释放,于是产生内存管理错误。

results matching ""

    No results matching ""