Block In OC
block 分為以下三種:
-
_NSConcreteStackBlock:棧block,引用了自動變量的block; -
_NSConcreteMallocBlock:堆block,棧block執(zhí)行copy得到的block; -
_NSConcreteGlobalBlock:全局block,沒有引用自動變量的block。
捕獲規(guī)則:
- 捕獲動作發(fā)生在創(chuàng)建block時;
- 對于全局變量(靜態(tài)/非靜態(tài)):在作用域中,不需要捕獲;
- 對于靜態(tài)變量(非全局):引用拷貝;
- 對于自動變量:值拷貝;
- 對于block變量(
__block只能修飾自動變量):將變量包裝成為結(jié)構(gòu)體,引用拷貝該結(jié)構(gòu)體。
示例代碼:
typedef void(^Block)(void);
static int a = 1;
@implementation TestBlock
- (void)test {
static int b = 1;
int c = 1;
__block int d = 1;
NSObject *e = [NSObject new];
__weak NSObject *f = e;
__block NSObject *g = [NSObject new];
Block block = ^{
a = 2;
b = 2;
//c = 2; // error: Variable is not assignable
printf("c = %d", c);
d = 2;
printf("e -> %p", e);
printf("f -> %p", f);
printf("f -> %p", g);
};
block();
}
@end
等效于下面的代碼:
typedef void(^Block)(void);
static int a = 1;
struct _struct_d { int d; };
struct _struct_g { NSObject *g; };
void _impl_block(int *b, const int c, struct _struct_d *d,
__strong NSObject *e, __weak NSObject *f,
struct _struct_g *g) {
a = 2; // a為全局變量,可以直接訪問
*b = 2; // b為靜態(tài)變量,引用拷貝
//c = 2; //error
printf("c = %d", c); // c為自動變量,值拷貝,并且拷貝后的變量為常量
(*d).d = 2; // d為 __block 修飾的自動變量,將d包裝為 struct _struct_d *,然后拷貝該指針
printf("e -> %p", e);
printf("f -> %p", f);
printf("f -> %p", g);
}
struct _struct_block {
// 引用計數(shù)器。初始化之后引用計數(shù)為1;每增加一個引用時計數(shù)器+1;每減少一個引用時計數(shù)器-1;計數(shù)器為0時觸發(fā) free。
int _retainCount;
int *b;
const int c;
struct _struct_d *d;
__strong NSObject *e;
__weak NSObject *f;
struct _struct_g *g;
void (* _impl_block)(int *, const int,
struct _struct_d *, __strong NSObject *,
__weak NSObject *, struct _struct_g *);
};
@implementation TestBlock
- (void)test {
static int b = 1;
int c = 1;
// 將d轉(zhuǎn)為指向堆區(qū)內(nèi)存的指針(該堆區(qū)內(nèi)存的管理未給出)
struct _struct_d *d = (struct _struct_d *)malloc(sizeof(struct _struct_d));
(*d).d = 1;
__strong NSObject *e = [NSObject new];
__weak NSObject *f = e;
// 將g轉(zhuǎn)為指向堆區(qū)內(nèi)存的指針(該堆區(qū)內(nèi)存的管理未給出)
struct _struct_g *g = (struct _struct_g *)malloc(sizeof(struct _struct_g *));
(*g).g = [NSObject new];
struct _struct_block block = {
1, // 初始化 block 后,引用計數(shù)為 1
&b, // 靜態(tài)變量不會被釋放,引用拷貝即可
c, // 普通變量,值拷貝
d, // 指向堆區(qū)內(nèi)存的指針,值拷貝該指針即可
e, // 指針,值拷貝;由于是 __strong 類型,對象的引用計數(shù)會增加
f, // 指針,值拷貝;由于是 __weak 類型,對象的引用計數(shù)不變
g, // 指針,值拷貝;拷貝 struct _struct_g 的指針不會影響到 NSObject 對象的引用計數(shù)
_impl_block // 目標(biāo)函數(shù)
}; // 拷貝變量,發(fā)生在創(chuàng)建 block 時;由于引用了自動變量,所以該 block 是棧 block。
// 執(zhí)行 block,不需要從上下文中獲取變量
block._impl_block(block.b, block.c, block.d, block.e, block.f, block.g);
block._retainCount --;
}
@end
總結(jié):
- 棧block的copy得到堆block,堆block和全局block的copy返回自身;
- 在ARC中,賦值操作會觸發(fā)棧block的copy,所以在ARC中大多數(shù)block是堆block;
- 在MRC中block捕獲的OC對象指針會觸發(fā)該對象的retain操作,防止出現(xiàn)這個現(xiàn)象的方法是:用
__block修飾OC對象指針,將指針包裝為結(jié)構(gòu)體,copy操作不會復(fù)制該結(jié)構(gòu)體; - 在ARC中block捕獲OC對象指針時,內(nèi)部的指針會繼承該指針的(
__strong/__unsafe_unretained/__weak)修飾符,所以防止block強引用捕獲的OC對象的方法是:用__weak獲取一個弱引用OC對象的指針,在block使用__strong獲取一個強引用OC對象的指針,防止block執(zhí)行過程中OC對象被釋放。
將OC編譯為C++的指令:
clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations block_arc.m -o block_arc.cpp
Closure In Swift
捕獲規(guī)則:
- 捕獲動作發(fā)生在創(chuàng)建closure時,捕獲動作分為直接捕獲和列表捕獲;
- 直接捕獲:引用拷貝,類似OC中
__block int或__block NSObject *的做法; - 列表捕獲非class:值拷貝,類似OC中
int的做法; - 列表捕獲class,拷貝對象的指針。根據(jù)使用的前綴不同,分為以下情況:
- 沒有前綴:得到對象的
__strong指針,對象引用計數(shù)+1; -
unowned前綴:得到對象的__unsafe_unretained指針,對象引用計數(shù)不變; -
weak前綴:得到對象的__weak指針,對象引用計數(shù)不變。
- 沒有前綴:得到對象的
函數(shù)轉(zhuǎn)變?yōu)?closure:
函數(shù)被變量引用時,會自動轉(zhuǎn)變?yōu)殚]包,并且執(zhí)行捕獲動作。
如果函數(shù)是實例的成員函數(shù),除常規(guī)捕獲外,還會捕獲該實例。
這是容易引發(fā)循環(huán)引用的原因之一。