block 寫法 1.block作為局部變量 2.block作為屬性3.block作為形參4.block作為實參
1.returnType (^blockName)(varType) = ^returnType (varType varName) {
// ...
};
2.@property (copy) returnType (^blockName) (varType);
3.- (void)yourMethod:(returnType (^)(varType))blockName
4.[someObject doSomethingWithBlock: ^returnType (varType varName)
{}];
Block三種類型及其原理
根據isa指針,block一共有3種類型的block
_NSConcreteGlobalBlock 全局靜態
_NSConcreteStackBlock 保存在棧中,出函數作用域就銷毀
_NSConcreteMallocBlock 保存在堆中,retainCount == 0銷毀
全局Block
/**
全局blcok ,內存全局區
未引用任何局部變量
*/
NSString *globalStr = @"全局blcok變量引用";
- (void)globalBlock {
//1、未引用任何外部變量
void (^globalBlockTest) (NSString *) = ^(NSString *global){
NSLog(@"%@",global);
};
NSLog(@"%@",globalBlockTest);
//2、引用全局變量
void (^globalBlockTest1)(void) = ^(){
NSLog(@"%@",globalStr);
};
NSLog(@"%@",globalBlockTest1);
globalBlockTest1();
}
控制臺打印結果:
2018-03-02 10:20:14.525730+0800 block[83814:624878] <NSGlobalBlock: 0x10633f088>
2018-03-02 10:20:14.525955+0800 block[83814:624878] <NSGlobalBlock: 0x10633f0c8>
2018-03-02 10:20:14.526089+0800 block[83814:624878] 全局blcok變量引用
堆區block(NSMallocBlock)
/**
堆區 block , 引用局部變量的block
*/
- (void) mallocBlock {
//局部變量
NSString *mallocBlockStr = @"堆區block局部變量";
void (^mallocBlock)(void) = ^(){
NSLog(@"%@", mallocBlockStr);
};
NSLog(@"%@", mallocBlock);
mallocBlock();
}
控制臺打印結果:
2018-03-02 10:30:31.140629+0800 block[84020:634734] <NSMallocBlock: 0x604000445940>
2018-03-02 10:30:31.140802+0800 block[84020:634734] 堆區block局部變量
棧區Block (NSStackBlock)
/**
棧區block
*/
- (void)stackBlock
{
NSString *stackBlockStr = @"棧區block變量";
void (^stackBlock)(void) = ^{
NSLog(@"%@", stackBlockStr);
};
// ARC下 不將block賦值給 strong引用時。打印的block就是 NSStackBlock
NSLog(@"block is %@", ^{
NSLog(@"%@", stackBlockStr);
});
// ARC下 將block賦值給 strong引用時。打印的block就是 NSMallocBlock
NSLog(@"block is %@", stackBlock);
}
控制臺打印結果:
2018-03-02 10:51:45.442501+0800 block[84372:653396] block is <NSStackBlock: 0x7ffeec18bb50>
2018-03-02 10:51:45.442745+0800 block[84372:653396] block is <NSMallocBlock: 0x604000447290>
第一個打印可看出block是一個 NSStackBlock, 即在棧上, 當函數返回時block將無效
第二個打印在非arc中打印是 NSStackBlock, 但是在arc中就是NSMallocBlock
即在arc中默認會將block從棧復制到堆上,而在非arc中,則需要手動copy.
其實在ARC下也是 NSStackBlock ,只是當把這個stackBlock賦值給strong應用后,ARC會自動給你copy 所以你在控制臺看到的是NSMallocBlock。
Block內存管理
Block自身內存管理
對于block,有兩個內存管理方法:Block_copy, Block_release;Block_copy與copy等效, Block_release與release等效;
不管是對block進行retian,copy,release,block的引用計數都不會增加,始終為1;
NSGlobalBlock:使用retain,copy, release都無效,block依舊存在全局區,且沒有釋放, 使用copy和retian只是返回block的指針;
NSStackBlock:使用retain,release操作無效;棧區block會在方法返回后將block空間回收; 使用copy將棧區block復制到堆區,可以長久保留block的空間,以供后面的程序使用;
NSMallocBlock:支持retian,release,雖然block的引用計數始終為1,但內存中還是會對引用進行管理,使用retain引用+1, release引用-1; 對于NSMallocBlock使用copy之后不會產生新的block,只是增加了一次引用,類似于使用retian;
對引用變量的內存管理
在block中經常會用到外部變量/對象,如果這個block是存儲在堆區,或者被復制到堆區,則對象對應的實例引用+1,當block釋放后block的引用-1;
循環引用
因為block中會對引用的對象進行持有(引用計數+1),從而導致相互持有引起循環引用;解決這種問題的方式是對引用變量使用修飾詞__block或者__weak;
- __block:在非ARC中使用,NSMallocBlock類型的block不會對__block修飾的的變量引用計數+1,從而消除循環引用;在ARC中使用__block無效
- __weak:在ARC中使用,作用和__block一樣,從而消除循環引用;在非ARC中不可以使用__weak;