在沒有訪問局部變量的情況下胸懈,Block的本質(zhì)類似于一個閉包交汤,它具有參數(shù)和返回值雏赦,并可以像普通函數(shù)一樣存儲在全局區(qū),被稱為"全局Block"芙扎。
如果我們在一個函數(shù)內(nèi)部創(chuàng)建了一個block星岗,該block內(nèi)部代碼引用了該函數(shù)內(nèi)的某些局部變量(此時該block為棧block(stackBlock)
),為了防止在block調(diào)用時引用的局部變量在原始作用域被銷毀戒洼,我們需要把block以及該局部變量copy到堆上(此時該block為堆block(mallocBlock)
)俏橘,以確保block可以被正常執(zhí)行。
auto變量 & static變量
// 在聲明變量時圈浇,如果沒有添加static關(guān)鍵字寥掐,那么默認(rèn)為auto變量,在函數(shù)調(diào)用結(jié)束后自動銷毀
(auto) int a = 10;
// 靜態(tài)變量磷蜀,儲存于全局區(qū)召耘,方法調(diào)用結(jié)束后不會銷毀
static int a = 10;
1.全局block
如果block中沒有訪問任何外部變量,則將被存儲在全局區(qū)中褐隆,不會被銷毀(這種不訪問外部變量的情況下我們一般會直接定義方法)
- (void)viewDidLoad {
[super viewDidLoad];
void (^block1)(void) = ^{};
NSLog(@"%@", [block1 class]); // __NSGlobalBlock__
}
2.MRC模式中的棧block
如果block中引用了auto變量
, 那么block的將被存儲在棧中污它。在MRC模式下方法調(diào)用結(jié)束后block的內(nèi)存會被回收。
- (void)viewDidLoad {
[super viewDidLoad];
int age = 10;
void (^block1)(void) = ^(){
NSLog(@"%d", age);
};
NSLog(@"%@", [block1 class]); // __NSStackBlock__
}
2.1 MRC模式中棧block導(dǎo)致的問題
由于stackBlock
引用的block在getBlock
方法執(zhí)行完成后就被銷毀了庶弃,所以當(dāng)在viewDidLoad
方法中調(diào)用stackBlock
時衫贬,age
的值發(fā)生了錯誤
#import "ViewController.h"
typedef void (^Myblock)(void);
Myblock stackBlock;
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self getBlock];
// __NSStackBlock__
NSLog(@"%@", [stackBlock class]);
// -307953584
stackBlock();
}
- (void)getBlock {
int age = 100;
stackBlock = ^(){
NSLog(@"%d", age);
};
}
@end
2.1 解決MRC模式中棧block導(dǎo)致的問題
在getBlock
方法中將棧block
賦值給stackBlock
時,我們對棧block進行一次copy
操作歇攻,這樣就會將該棧block的副本存儲在堆中固惯,該副本不會隨著方法執(zhí)行結(jié)束而銷毀。
- (void)viewDidLoad {
[super viewDidLoad];
[self getBlock];
// __NSMallocBlock__
NSLog(@"%@", [stackBlock class]);
// -100
stackBlock();
}
- (void)getBlock {
int age = 100;
stackBlock = [^(){
NSLog(@"%d", age);
} copy];
}
3 堆block
在MRC模式下缴守,堆Block是由棧Block拷貝而來葬毫,需要程序員手動調(diào)用copy方法來實現(xiàn)。
而在ARC模式下斧散,為了安全起見供常,xcode會對棧block的賦值行為自動進行一次copy操作(即使賦值給以Strong修飾的成員變量,依然會進行一次copy操作鸡捐,在堆中創(chuàng)建一個新副本)