下面的講解均是在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和內存管理的工作。