Block的本質(zhì)
Block的本質(zhì)是一個OC對象,它內(nèi)部也有一個isa指針,
Block是封裝了函數(shù)調(diào)用和函數(shù)調(diào)用環(huán)境的OC對象
Block的底層結(jié)構(gòu)

image1.png
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5c_f1wlx5wn2nv1zl04kg_35prm0000gn_T_main_54fbf7_mi_0);
}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
}
return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };
Block底層數(shù)據(jù)結(jié)構(gòu)中有一個struct __block_impl impl和struct __main_block_desc_0* Desc
- __block_impl 中
isa指針和void *FuncPtr封裝函數(shù) - __main_block_desc_0中是Block的描述信息
Block捕獲變量
為了保證block內(nèi)部能夠正常訪問外部的變量,Block內(nèi)部有個變量捕獲機制

image2.png

image3.png
上圖顯示的是三種變量的捕獲,大家看到局部變量a在block中無法被修改,但是用static修飾的b和全局變量c可以直接修改,這是為什么呢???
我們使用clang編譯器編譯一下block的底層看看

image4.png
變量block內(nèi)部竟然直接引用的是變量的a的值和變量b的指針。
現(xiàn)在從堆棧上解釋一下為什么:
- block內(nèi)部封裝了一個函數(shù),而變量a是屬于main函數(shù)的局部變量,如果在block中更改a的值的話,在棧中是沒法調(diào)用另一個函數(shù)的局部變量
- 使用static修飾的變量b,由于是指針引用,完全可以通過這個指針找到b,
- c是屬于全局變量,block是完全可以找到的
Block的類型
Block有3中類型,可以通過class方法或者isa指針查看具體類型,最終都是繼承自NSBlock類型,直到NSObject。
- NSGlobalBlock
- NSStackBlock
-
NSMallocBlock
image5.png
image6.png
image7.png

image8.png
在ARC中編譯器會根據(jù)集中情況,自動將棧上的block復(fù)制到堆上
- block作為函數(shù)返回值時候
- 將block賦值給__strong指針時候
- block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時
- block作為GCD API的方法參數(shù)時
Block捕獲對象類型
當(dāng)block內(nèi)部訪問了對象類型的auto變量時
如果block在棧上,將不會對auto變量產(chǎn)生強引用
如果block被copy到堆上
- 會調(diào)用block內(nèi)部的copy函數(shù),
- _Block_object_assign函數(shù)會根據(jù)auto變量的修飾符(__strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強引用(retain)或者弱引用
#######如果block從堆上移除 - 會調(diào)用block內(nèi)部的dispose函數(shù)
- dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
-
_Block_object_dispose函數(shù)會自動釋放引用的auto變量(release)
image8.png
在使用clang轉(zhuǎn)換OC為C++代碼時,可能會遇到以下問題
cannot create __weak reference in file using manual reference
解決方案:支持ARC、指定運行時系統(tǒng)版本,比如
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
__block修飾符
- __block可以用于解決block內(nèi)部無法修改auto變量值的問題
- __block不能修飾全局變量、靜態(tài)變量(static)
- 編譯器會將__block變量包裝成一個對象
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_a_0 *a; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_a_0 *_a, int flags=0) : a(_a->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

image9.png
__block的內(nèi)存管理
- 當(dāng)block在棧上時,并不會對__block變量產(chǎn)生強引用
- 當(dāng)block被copy到堆時
會調(diào)用block內(nèi)部的copy函數(shù)
copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會對__block變量形成強引用(retain) -
當(dāng)block從堆中移除時
會調(diào)用block內(nèi)部的dispose函數(shù)
dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
_Block_object_dispose函數(shù)會自動釋放引用的__block變量(release)
image10.png
image11.png
__block的__forwarding指針

image12.png
對象類型的auto變量、__block變量
- 當(dāng)block在棧上時,對它們都不會產(chǎn)生強引用
- 當(dāng)block拷貝到堆上時,都會通過copy函數(shù)來處理它們
__block變量(假設(shè)變量名叫做a)
_Block_object_assign((void)&dst->a, (void)src->a, 8/BLOCK_FIELD_IS_BYREF/); - 對象類型的auto變量(假設(shè)變量名叫做p)
_Block_object_assign((void)&dst->p, (void)src->p, 3/BLOCK_FIELD_IS_OBJECT/); - 當(dāng)block從堆上移除時,都會通過dispose函數(shù)來釋放它們
__block變量(假設(shè)變量名叫做a)
_Block_object_dispose((void)src->a, 8/BLOCK_FIELD_IS_BYREF*/); - 對象類型的auto變量(假設(shè)變量名叫做p)
_Block_object_dispose((void)src->p, 3/BLOCK_FIELD_IS_OBJECT*/);
被__block修飾的對象類型
- 當(dāng)__block變量在棧上時,不會對指向的對象產(chǎn)生強引用
- 當(dāng)__block變量被copy到堆時
會調(diào)用__block變量內(nèi)部的copy函數(shù)
copy函數(shù)內(nèi)部會調(diào)用_Block_object_assign函數(shù)
_Block_object_assign函數(shù)會根據(jù)所指向?qū)ο蟮男揎椃╛_strong、__weak、__unsafe_unretained)做出相應(yīng)的操作,形成強引用(retain)或者弱引用(注意:這里僅限于ARC時會retain,MRC時不會retain) - 如果__block變量從堆上移除
會調(diào)用__block變量內(nèi)部的dispose函數(shù)
dispose函數(shù)內(nèi)部會調(diào)用_Block_object_dispose函數(shù)
_Block_object_dispose函數(shù)會自動釋放指向的對象(release)
循環(huán)引用

image13.png

image14.png
解決循環(huán)引用問題 - MRC

image.png





