陈江川

邮箱:jiangchuanc@gmail.com

Objective-C中的Block<二>

注意:文中涉及到的代码及结论如果没有特别说明,那么都是在MRC环境下测试!!!

Objective-C中的Block<一>中,我们知道了Block的三种类,以及在Block中如何对变量的值进行修改。那么这节我们深入的讨论Block三个类的使用。

以下例子都出自Objective-C Blocks Quiz

Example A

void exampleA() {
    char a = 'A';
    void(^blk)() = ^{
        NSLog(@"%c", a);
    };
    NSLog(@"%@", blk);
}

int main(int argc, const char * argv[]) {
    exampleA();
    return 0;
}

分析:

  1. blk属于_NSStackBlock,因为blk内部访问了局部变量,更确切的说是访问了栈区变量;
  2. 该示例可以在MRC和ARC下运行。

Example B

void exampleB_addBlockToArray(NSMutableArray *array) {
    char b = 'B';
    void (^blk)() = ^{
        printf("%cn", b);
    };
    NSLog(@"%@", blk);
    [array addObject:blk];
}

void exampleB() {
    NSMutableArray *array = [NSMutableArray array];
    exampleB_addBlockToArray(array);
    void (^block)() = [array objectAtIndex:0];
    block();
}

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

    exampleB();
    return 0;
}

分析:

  1. 我们一定要清楚exampleB_addBlockToArray()函数中blk属于哪个Block类? blk中我们访问了栈区的变量,所以blk属于_NSStackBlock,也就是说blk的作用域只在 exampleB_addBlockToArray()函数中有效,函数跑完后,blk就会被释放!!!
  2. exampleB()函数中我们访问exampleB_addBlockToArray()中的blk,上面已经说明,blk在函数结束的时候已经被释放掉,所以exampleB()访问了一块不存在地址,程序崩溃。
  3. 那怎么才能在exampleB()中访问blk?有两种方法:

    • 改变char b = 'B';的所在内存地址,有三种方式:

      • 定义为全局变量
      • 定义为静态全局变量
      • 定义为静态局部变量
    • 使用copy或者Block_copy,把blk从栈区复制到堆区,一定要注意:有copy,一定要有release,否则会造成内存泄漏。

    void exampleB_addBlockToArray(NSMutableArray *array) {
        char b = 'B';
        void (^blk)() = ^{
            printf("%cn", b);
        };
    // blk = [blk copy];
        blk = Block_copy(blk);
        NSLog(@"%@", blk);
        [array addObject:blk];
    }
    
    void exampleB() {
        NSMutableArray *array = [NSMutableArray array];
        exampleB_addBlockToArray(array);
        void (^block)() = [array objectAtIndex:0];
        NSLog(@"block %@", block);
    // [block release];
        Block_release(block);
    }
    

Example C

void exampleC_addBlockToArray(NSMutableArray *array) {
    void (^blk)() = ^{
        printf("Cn");
    };
    NSLog(@"%@", blk);
    [array addObject:blk];
}

void exampleC() {
    NSMutableArray *array = [NSMutableArray array];
    exampleC_addBlockToArray(array);
    void (^block)() = [array objectAtIndex:0];
    NSLog(@"block %@", block);
}

分析:
exampleC_addBlockToArray()函数中的blk属于_NSGlobalBlock,所以外界可以访问该Block。

Example D

typedef void (^dBlock)();

dBlock exampleD_getBlock() {
    char d = 'D';
    void (^blk)() = ^{
        printf("%cn", d);
    };

    return blk;
}

void exampleD() {
    dBlock block = exampleD_getBlock();
    NSLog(@"%@", block);
}

分析:
exampleD_getBlock()函数中的blk,因为Block内存访问了栈区的变量,所以blk属于_NSStackBlock,也就是说blk的作用域只在该函数中有效,所以在exampleD()函数接收blk,程序崩溃,因为访问了一块不存在的内存地址!!!
解决方法同Example B

« Objective-C中的Block<三> Objective-C中的Block<一> »