从堆栈剖析block

下面的讲解均是在MRC下进行,首先,需设置-fno-objc-arc。

两概念


  • 内存需要手动分配(malloc)和销毁(free)。

  • 一个线程会分配一个stack,里面包含这个函数参数,局部变量,返回地址等信息。当函数返回后,栈会被销毁,有系统操作。 后面再去retain和copy是无效的。
    栈对象有速度的优势,不会发生内存泄漏,

存储区域

通过查看block的isa值,可以看到以下3个名词:

  • NSGlobalBlock 全局
  • NSStackBlock 栈
  • NSMallocBlock 堆

代码演示

//全局block
- (dispatch_block_t)testGlobalBlock {
    dispatch_block_t block = ^(){
        NSLog(@"global block");
    };
    return block;
//或
//    return [block copy];//仍是全局    
}

//栈block
- (dispatch_block_t)testStackBlock {
    __block NSInteger i = 0;
    dispatch_block_t block = ^() {
        NSLog(@"%ld", ++i);
    };
    return block;
}
dispatch_block_t block = [self testStackBlock];
block(); 会crash
dispatch_block_t copyBlock = [block copy];
copyBlock(); 依然crash
//堆 block
- (dispatch_block_t)testMallocBlock {
    __block NSInteger i = 0;
    dispatch_block_t block = ^() {
        NSLog(@"%ld", ++i);
    };
    return [block copy];
}
  • 全局,不访问外部任何变量,copy后仍是全局block;
  • 栈,block里有访问外部变量,函数返回后立即销毁,即使后面strong和copy都会crash;
  • 堆,有栈block拷贝过来,就和OC对象一样,可访问外部变量;

block为什么不用strong

@property (nonatomic, strong) NSString *testStr;
@property (nonatomic, strong) dispatch_block_t strongBlock;

testStr = @"aaa";
- (void)testStackBlock {
    __block NSInteger i = 0;
    //注意此处是self.而不是_strongBlock
    self.strongBlock = ^() {
        NSLog(@"%ld", ++i);
        NSLog(@"%@", _testStr);
    };
    self.copyBlock = ^() {
        NSLog(@"%ld", ++i);
        NSLog(@"%@", _testStr);
    };
}


[self testStackBlock];
_strongBlock(); //堆
_copyBlock(); //堆

栈blog不能访问全局对象,只有堆blog。因为strong和copy均是copy,从语义上更贴切

ARC

在ARC下,即使你声明的修饰符是strong,实际上效果是与声明为copy一样的。因此在ARC情况下,创建的block仍然是NSStackBlock类型,只不过当block被引用或返回时,ARC完成了copy和内存管理的工作。

你可能感兴趣的:(从堆栈剖析block)