最近發(fā)現(xiàn)很多開發(fā)者對(duì)block的理解并不是很深,很多項(xiàng)目當(dāng)中使用的時(shí)候多多少會(huì)有些問題,今天給大家詳細(xì)講講block的內(nèi)存管理, 主要從以下幾個(gè)方面來講:
- 根據(jù)內(nèi)存劃分block的類型
- block內(nèi)存管理
- 防止循環(huán)引用
B?lock類型
根據(jù)Block在內(nèi)存中的位置,系統(tǒng)把Block分為3類:NSGlobalBlock,NSStackBlock, NSMallocBlock;
-
NSGlobalBlock:位于內(nèi)存全局區(qū) -
NSStackBlock:位于內(nèi)存棧區(qū) -
NSMallocBlock:位于內(nèi)存堆區(qū)
我們通過block引用不同的變量來
全局區(qū)block(NSGlobalBlock)
沒有引用局部變量的block叫做NSGlobalBlock,如下實(shí)例:
//類型1:沒有使用任何外部變量
-(void)test
{
void (^gBlock1)(int , int ) =^(int a, int b){
NSLog(@"a + b = %d", a+b);
};
NSLog(@"%@", gBlock1);
//打印結(jié)果為:
//<__NSGlobalBlock__: 0x1025e8110>
}
//類型2:使用全局變量
//全局變量
int a = 10;
-(void)test
{
void (^gBlock)() = ^(){
NSLog(@"%d", a);
};
NSLog(@"%@", gBlock);
//輸出結(jié)果為:
//<__NSGlobalBlock__: 0x103676110>
}
棧區(qū)block(NSStackBlock)
引用了局部變量的block叫做NSStackBlock, 實(shí)例如下:
-(void)test
{
//局部變量
NSArray *arr = @[@"zhangsan", @"lisi"];
void (^sBlock)() = ^(){
NSLog(@"arr = %@", arr);
};
NSLog(@"%@", sBlock);
//輸出結(jié)果為:
//<__NSStackBlock__: 0x7fff5bbf1a58>
}
PS:棧區(qū)block在方法返回后就會(huì)被釋放,所以只能在方法內(nèi)部使用,如果將他賦值給其他對(duì)象或者存儲(chǔ)起來,后面使用時(shí)將會(huì)出現(xiàn)錯(cuò)誤.
堆區(qū)Block(***NSMallocBlock ***)
在非ARC下,我們一般不手動(dòng)創(chuàng)建NSMallocBlock,我們把從棧區(qū)復(fù)制(copy)過來的block稱為堆區(qū)block。實(shí)例如下:
-(void)test
{
NSArray *arr = @[@"zhangsan", @"lisi"];
//棧區(qū)block
void (^sBlock)() = ^(){
NSLog(@"arr = %@", arr);
};
NSLog(@"%@", sBlock);
//堆區(qū)block
void (^mBlock)() = [sBlock copy];
NSLog(@"%@", mBlock);
//輸出結(jié)果為:
//<__NSStackBlock__: 0x7fff59bf9a38>
//<__NSMallocBlock__: 0x7fc173f0dd80>
}
Block內(nèi)存管理
對(duì)block自身內(nèi)存的管理
對(duì)于block,有兩個(gè)內(nèi)存管理方法:Block_copy, Block_release;Block_copy與copy等效, Block_release與release等效;
不管是對(duì)block進(jìn)行retian,copy,release,block的引用計(jì)數(shù)都不會(huì)增加,始終為1;
NSGlobalBlock:使用retain,copy,release都無效,block依舊存在全局區(qū),且沒有釋放, 使用copy和retian只是返回block的指針;NSStackBlock:使用retain,release操作無效;棧區(qū)block會(huì)在方法返回后將block空間回收; 使用copy將棧區(qū)block復(fù)制到堆區(qū),可以長(zhǎng)久保留block的空間,以供后面的程序使用;NSMallocBlock:支持retian,release,雖然block的引用計(jì)數(shù)始終為1,但內(nèi)存中還是會(huì)對(duì)引用進(jìn)行管理,使用retain引用+1,release引用-1; 對(duì)于NSMallocBlock使用copy之后不會(huì)產(chǎn)生新的block,只是增加了一次引用,類似于使用retian;
對(duì)引用變量的內(nèi)存管理
在block中經(jīng)常會(huì)用到外部變量/對(duì)象,如果這個(gè)block是存儲(chǔ)在堆區(qū),或者被復(fù)制到堆區(qū),則對(duì)象對(duì)應(yīng)的實(shí)例引用+1,當(dāng)block釋放后block的引用-1;
-(void)test
{
NSArray *arr = @[@"zhangsan", @"lisi"];
NSLog(@"arr.retianCount = %ld", arr.retainCount);
//棧區(qū)block
void (^sBlock)() = ^(){
NSLog(@"arr = %@", arr);
};
//棧區(qū)block不會(huì)對(duì)引用的變量引用計(jì)數(shù)+1
NSLog(@"arr.retianCount = %ld", arr.retainCount);
//堆區(qū)block
void (^mBlock)() = [sBlock copy];
//復(fù)制到堆區(qū)后,引用計(jì)數(shù)+1
NSLog(@"arr.retianCount = %ld", arr.retainCount);
}
循環(huán)引用
因?yàn)閎lock中會(huì)對(duì)引用的對(duì)象進(jìn)行持有(引用計(jì)數(shù)+1),從而導(dǎo)致相互持有引起循環(huán)引用;解決這種問題的方式是對(duì)引用變量使用修飾詞__block或者__weak;
-
__block:在非ARC中使用,NSMallocBlock類型的block不會(huì)對(duì)__block修飾的的變量引用計(jì)數(shù)+1,從而消除循環(huán)引用;在ARC中使用__block無效 -
__weak:在ARC中使用,作用和__block一樣,從而消除循環(huán)引用;在非ARC中不可以使用__weak;
防止循環(huán)引用案例:
//TestClass.h
@interface testClass : NSObject
@property (nonatomic, copy)void (^myBlock)(void);
@end
//TestClass.m
#define TestClassExample3 1
@implementation TestClass
-(void)dealloc
{
NSLog(@"測(cè)試對(duì)象 被釋放了。。。");
[super dealloc];
}
-(instancetype)init
{
self = [super init];
if (self) {
#if TestClassExample1
//會(huì)引起循環(huán)應(yīng)用,當(dāng)前對(duì)象無法被釋放
self.myBlock = ^(){
//增加自己本身的引用計(jì)數(shù)
[self doSomething];
};
#elif TestClassExample2
//在非ARC下有效,防止循環(huán)引用
//在ARC下無效,會(huì)產(chǎn)生循環(huán)引用
__block TestClass *weakSelf = self;
self.myBlock = ^(){
//在非ARC下不會(huì)增加self的引用計(jì)數(shù)
[weakSelf doSomething];
};
#elif TestClassExample3
//在非ARC下無效,會(huì)產(chǎn)生循環(huán)引用
//在ARC下有效,防止循環(huán)應(yīng)用
__weak TestClass *weakSelf = self;
self.myBlock = ^(){
//在非ARC下不會(huì)增加self的引用計(jì)數(shù)
[weakSelf doSomething];
};
#endif
}
return self;
}
-(void)doSomething
{
NSLog(@"測(cè)試程序");
}
@end
//main.h
int main(int argc, char * argv[]) {
@autoreleasepool {
TestClass *tc = [[TestClass alloc] init];
[tc release];
tc = nil;
}
}
歡迎大家踴躍評(píng)論,讓我們一起探討技術(shù)!!
如果覺得文章不錯(cuò),請(qǐng)幫忙點(diǎn)擊文章下方的喜歡??!
你的支持將是對(duì)我最好的鼓勵(lì), 謝謝?。?!