下面的讲解均是在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和内存管理的工作。
网友评论