上文說到,block本質(zhì)是一個(gè)可以捕獲變量的匿名函數(shù)坑质,并且具有對(duì)象的特征,也可以看做是一個(gè)僅有一個(gè)函數(shù)的對(duì)象。接下來就通過Clang和查看源碼分析block的底層實(shí)現(xiàn)來證明
主要內(nèi)容:
1芹敌、block的本質(zhì)
2、__block的原理
3垮抗、block的底層類型
4氏捞、block從棧拷貝到堆的過程分析
1冒版、block的本質(zhì)
1.1 block在底層的結(jié)構(gòu)
定義block.c文件
#include "stdio.h"
int main(){
void(^block)(void) = ^{
printf("wy");
};
return 0;
}
通過Clang編譯
int main(){
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("wy");
}
//******簡(jiǎn)化******
void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA));//構(gòu)造函數(shù)
block->FuncPtr(block);//block調(diào)用執(zhí)行
說明:
- 可以看出__main_block_impl_0用來構(gòu)造block
- __main_block_impl_0函數(shù)傳入兩個(gè)參數(shù)液茎,__main_block_func_0和&__main_block_desc_0_DATA。
- 通過指向block的FuncPtr方法進(jìn)行調(diào)用
查看結(jié)構(gòu)體
//**block定義結(jié)構(gòu)體**
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;
}
};
//**block實(shí)現(xiàn)結(jié)構(gòu)體**
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
說明:
- 可以看到__main_block_impl_0結(jié)構(gòu)體是用以定義block的一個(gè)結(jié)構(gòu)體辞嗡。包含block結(jié)構(gòu)體捆等、block描述信息,以及一個(gè)同名構(gòu)造函數(shù)
- impl是block本身
- Desc會(huì)提供一些定義block時(shí)的描述信息
- 在構(gòu)造函數(shù)中會(huì)將外界的值賦值到block結(jié)構(gòu)體中续室。以此來構(gòu)造block
- 重點(diǎn)看的就是給impl的isa賦值block類型栋烤,還有將外界的函數(shù)賦值給block。
- 在block結(jié)構(gòu)體__block_impl中包含isa和FuncPtr挺狰,還有Flags和Reserved(這兩個(gè)不用關(guān)注)
- 結(jié)構(gòu)體中包含有isa班缎,有三種類型,NSGlobalBlock她渴、NSMallocBlock达址、NSStackBlock,這也可以說明block可以看做一個(gè)對(duì)象趁耗,因?yàn)樗衖sa沉唠,并且指向不同的結(jié)構(gòu)體類型。
- FuncPtr就是block中的函數(shù)苛败,它用來執(zhí)行具體的功能
1.2 block如何捕獲外界變量
定義一個(gè)變量满葛,并在block中調(diào)用
int main(){
int a = 11;
void(^block)(void) = ^{
printf("WY - %d", a);
};
block();
return 0;
}
底層編譯:
//**block實(shí)現(xiàn)結(jié)構(gòu)體**
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
//block定義結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;//編譯時(shí)就自動(dòng)生成了相應(yīng)的變量
// a(_a) 的做法是將傳入的_a賦值給當(dāng)前結(jié)構(gòu)體中的a
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
impl.isa = &_NSConcreteStackBlock;//block的isa默認(rèn)是stackBlock
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block函數(shù)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//在函數(shù)中會(huì)自動(dòng)創(chuàng)建一個(gè)局部變量,并將外界的變量值賦值給局部變量a中
//屬于值拷貝罢屈,也就是直接將11賦值給a.因此__cself->a和a沒有關(guān)系
int a = __cself->a; // bound by copy 值拷貝嘀韧,即 a = 11
printf("WY - %d", a);
}
int main(){
int a = 11;
//當(dāng)block使用外界變量時(shí),會(huì)自動(dòng)將變量傳入到block中
void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a));
block)->FuncPtr(block);
return 0;
}
說明:
- 當(dāng)block使用外界的變量時(shí)缠捌,會(huì)將變量通過block的構(gòu)造函數(shù)中傳入到block定義結(jié)構(gòu)體中
- 在block的構(gòu)造函數(shù)中會(huì)將變量賦值給block定義結(jié)構(gòu)體__main_block_impl_0的變量锄贷。(此時(shí)_main_block_impl_0結(jié)構(gòu)體會(huì)自動(dòng)創(chuàng)建一個(gè)相同名稱的變量,差別在于沒有前邊沒有)
- 之后在block函數(shù)中也會(huì)自動(dòng)創(chuàng)建一個(gè)局部變量译蒂,同時(shí)將__main_block_impl_0結(jié)構(gòu)體中的變量賦值給局部變量。以此讓函數(shù)可以使用該變量谊却。
總結(jié):block在使用外界變量時(shí)柔昼,會(huì)在block定義結(jié)構(gòu)體中定義一個(gè)變量來保存外界變量的值。這樣就表現(xiàn)為捕獲炎辨。
1.3 __block修飾變量的原理
對(duì)a加一個(gè)__block捕透,然后在block中使用a
#include "stdio.h"
int main(){
__block int a = 11;
void(^block)(void) = ^{
a++;
printf("WY %d",a);
};
// block();
return 0;
}
Clang編譯:
//block實(shí)現(xiàn)結(jié)構(gòu)體
struct __block_impl {
void *isa;//可以看到包含有isa
int Flags;
int Reserved;
void *FuncPtr;//函數(shù)指針
};
//__block修飾的變量結(jié)構(gòu)體
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;//這里是a的地址
int __flags;
int __size;
int a;//最后可以找到結(jié)構(gòu)中的變量a
};
//定義block的底層結(jié)構(gòu)體
struct __main_block_impl_0 {
struct __block_impl impl;//block結(jié)構(gòu)體
struct __main_block_desc_0* Desc;//描述符
__Block_byref_a_0 *a; // 變量,在編譯時(shí)就會(huì)將外界的數(shù)據(jù)獲取到
//構(gòu)造函數(shù)碴萧,傳入的fp就是block內(nèi)部的函數(shù)
__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;//這里是棧block
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block內(nèi)部的函數(shù)實(shí)現(xiàn)乙嘀,會(huì)傳入block本身
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
//將block的變量賦值給當(dāng)前函數(shù)的局部變量。
//但因?yàn)榇颂幍腶是指針破喻,不是基本類型虎谢,所以指針賦值的修改是可以影響的。
//在函數(shù)中是定義了一個(gè)變量a低缩,如果是基本數(shù)據(jù)類型嘉冒,進(jìn)行賦值曹货,那么就是局部變量咆繁,無法影響到外界的a
__Block_byref_a_0 *a = __cself->a; // bound by ref
//之后將結(jié)構(gòu)體a的__forwarding結(jié)構(gòu)體拿到,在拿到結(jié)構(gòu)體中的變量a,之后進(jìn)行操作
//也就是說這里操作的不是局部變量本身顶籽,而是局部變量結(jié)構(gòu)體的指針玩般。
(a->__forwarding->a)++;
printf("LG_Cooci - %d",(a->__forwarding->a));
}
int main(){
//__block修飾的變量
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 11};
//block定義
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
//這里是調(diào)用,可以看到會(huì)將結(jié)構(gòu)體__Block_byref_a_0作為變量傳入到block中
void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
return 0;
}
說明:
- 可以看到外界的變量使用__block修飾后礼饱,其實(shí)在底層會(huì)將該變量設(shè)置為結(jié)構(gòu)體__Block_byref_a_0
- __Block_byref_a_0結(jié)構(gòu)體主要包含兩個(gè)值坏为,一個(gè)是變量地址,一個(gè)是變量本身
- block在使用變量時(shí)镊绪,也會(huì)傳入__Block_byref_a_0結(jié)構(gòu)體來使用匀伏,可以看到傳入的其實(shí)就是a的地址&a。
- __main_block_impl_0中也會(huì)主動(dòng)創(chuàng)建一個(gè)變量block來存儲(chǔ)外界變量的地址蝴韭,通過a(_a->__forwarding)來進(jìn)行賦值
- 最后在block內(nèi)部函數(shù)__main_block_func_0中也自動(dòng)創(chuàng)建一個(gè)__Block_byref_a_0局部變量用來存儲(chǔ)外界變量的地址够颠。
- 在函數(shù)中使用變量時(shí),其實(shí)使用的是通過變量地址所指向的變量榄鉴。
總結(jié):
- 外界變量會(huì)生成__Block_byref_a_0結(jié)構(gòu)體
- 結(jié)構(gòu)體用來保存原始變量的指針和值
- 將變量生成的結(jié)構(gòu)體對(duì)象的指針地址 傳遞給block履磨,然后在block內(nèi)部就可以對(duì)外界變量進(jìn)行操作了
- 簡(jiǎn)單來說就是將值拷貝轉(zhuǎn)變?yōu)橹羔樋截悾跃涂梢孕薷耐饨缱兞苛恕?/li>
2庆尘、block的底層類型
查看libclosure-74源碼,通過查看_Block_copy的源碼實(shí)現(xiàn)剃诅,發(fā)現(xiàn)block在底層的真正類型是Block_layout
block真正類型
查看Block_layout類型的定義,是一個(gè)結(jié)構(gòu)體
// Block 結(jié)構(gòu)體
struct Block_layout {
//指向表明block類型的類
void *isa;//8字節(jié)
//用來作標(biāo)識(shí)符的驶忌,類似于isa中的位域,按bit位表示一些block的附加信息
volatile int32_t flags; // contains ref count 4字節(jié)
//保留信息矛辕,可以理解預(yù)留位置,用于存儲(chǔ)block內(nèi)部變量信息
int32_t reserved;//4字節(jié)
//函數(shù)指針,指向具體的block實(shí)現(xiàn)的調(diào)用地址
BlockInvokeFunction invoke;
//block的附加信息
struct Block_descriptor_1 *descriptor;
// imported variables
};
說明:
- isa:指向表明block類型的類
- flags:標(biāo)識(shí)符如筛,按bit位表示一些block的附加信息堡牡,使用位域的方式存儲(chǔ),其中flags的種類有以下幾種杨刨,主要重點(diǎn)關(guān)注BLOCK_HAS_COPY_DISPOSE 和 BLOCK_HAS_SIGNATURE晤柄。 BLOCK_HAS_COPY_DISPOSE 決定是否有 Block_descriptor_2。BLOCK_HAS_SIGNATURE 決定是否有 Block_descriptor_3
- 第1 位 - BLOCK_DEALLOCATING妖胀,釋放標(biāo)記芥颈,-般常用 BLOCK_NEEDS_FREE 做 位與 操作,一同傳入 Flags 赚抡, 告知該 block 可釋放爬坑。
- 低16位 - BLOCK_REFCOUNT_MASK,存儲(chǔ)引用計(jì)數(shù)的值;是一個(gè)可選用參數(shù)
- 第24位 - BLOCK_NEEDS_FREE涂臣,低16是否有效的標(biāo)志盾计,程序根據(jù)它來決定是否增加或是減少引用計(jì)數(shù)位的 值;
- 第25位 - BLOCK_HAS_COPY_DISPOSE,是否擁有拷貝輔助函數(shù)(a copy helper function);
- 第26位 - BLOCK_IS_GC赁遗,是否擁有 block 析構(gòu)函數(shù);
- 第27位署辉,標(biāo)志是否有垃圾回收;//OS X
- 第28位 - BLOCK_IS_GLOBAL,標(biāo)志是否是全局block;
- 第30位 - BLOCK_HAS_SIGNATURE岩四,與 BLOCK_USE_STRET 相對(duì)哭尝,判斷當(dāng)前 block 是否擁有一個(gè)簽名。用于 runtime 時(shí)動(dòng)態(tài)調(diào)用剖煌。
// Values for Block_layout->flags to describe block objects
enum {
//釋放標(biāo)記材鹦,一般常用于BLOCK_BYREF_NEEDS_FREE做位與運(yùn)算,一同傳入flags耕姊,告知該block可釋放
BLOCK_DEALLOCATING = (0x0001), // runtime
//存儲(chǔ)引用引用計(jì)數(shù)的 值桶唐,是一個(gè)可選用參數(shù)
BLOCK_REFCOUNT_MASK = (0xfffe), // runtime
//低16位是否有效的標(biāo)志,程序根據(jù)它來決定是否增加或者減少引用計(jì)數(shù)位的值
BLOCK_NEEDS_FREE = (1 << 24), // runtime
//是否擁有拷貝輔助函數(shù)茉兰,(a copy helper function)決定block_description_2
BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler
//是否擁有block C++析構(gòu)函數(shù)
BLOCK_HAS_CTOR = (1 << 26), // compiler: helpers have C++ code
//標(biāo)志是否有垃圾回收尤泽,OSX
BLOCK_IS_GC = (1 << 27), // runtime
//標(biāo)志是否是全局block
BLOCK_IS_GLOBAL = (1 << 28), // compiler
//與BLOCK_HAS_SIGNATURE相對(duì),判斷是否當(dāng)前block擁有一個(gè)簽名邦邦,用于runtime時(shí)動(dòng)態(tài)調(diào)用
BLOCK_USE_STRET = (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
//是否有簽名
BLOCK_HAS_SIGNATURE = (1 << 30), // compiler
//使用有拓展安吁,決定block_description_3
BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31) // compiler
};
- reserved:保留信息,可以理解預(yù)留位置燃辖,猜測(cè)是用于存儲(chǔ)block內(nèi)部變量信息
- invoke:是一個(gè)函數(shù)指針鬼店,指向block的執(zhí)行代碼
- descriptor:block的附加信息,比如保留變量數(shù)黔龟、block的大小妇智、進(jìn)行copy或dispose的輔助函數(shù)指針滥玷。有三類
- Block_descriptor_1是必選的
- Block_descriptor_2 和 Block_descriptor_3都是可選的
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
uintptr_t reserved;//保留信息
uintptr_t size;//block大小
};
#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;//拷貝函數(shù)指針
BlockDisposeFunction dispose;
};
#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
// requires BLOCK_HAS_SIGNATURE
const char *signature;//簽名
const char *layout; // contents depend on BLOCK_HAS_EXTENDED_LAYOUT 布局
};
以上關(guān)于descriptor的可以從其構(gòu)造函數(shù)中體現(xiàn),其中Block_descriptor_2和Block_descriptor_3都是通過Block_descriptor_1的地址巍棱,經(jīng)過內(nèi)存平移得到的
源碼:
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
return aBlock->descriptor;//默認(rèn)打印
}
#endif
// Block 的描述 : copy 和 dispose 函數(shù)
static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;//descriptor_1的地址
desc += sizeof(struct Block_descriptor_1);//通過內(nèi)存平移獲取
return (struct Block_descriptor_2 *)desc;
}
// Block 的描述 : 簽名相關(guān)
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
uint8_t *desc = (uint8_t *)aBlock->descriptor;
desc += sizeof(struct Block_descriptor_1);
if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
desc += sizeof(struct Block_descriptor_2);
}
return (struct Block_descriptor_3 *)desc;
}
4惑畴、block的拷貝過程
包括block本身從棧如何拷貝到堆中,捕獲的外界局部變量如何拷貝到堆中兩類航徙。
4.1 _Block_copy源碼分析
這里只有一次拷貝如贷,即將棧block拷貝為堆block,如果不使用外界的局部變量到踏,就只有這一層拷貝杠袱。
源碼
// Copy, or bump refcount, of a block. If really copying, call the copy helper if present.
// 這里是核心重點(diǎn) block的拷貝操作: 棧Block -> 堆Block
void *_Block_copy(const void *arg) {
struct Block_layout *aBlock;
if (!arg) return NULL;
// The following would be better done as a switch statement
aBlock = (struct Block_layout *)arg;//強(qiáng)轉(zhuǎn)為Block_layout類型對(duì)象,防止對(duì)外界造成影響
if (aBlock->flags & BLOCK_NEEDS_FREE) {//是否需要釋放
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
else if (aBlock->flags & BLOCK_IS_GLOBAL) {//如果是全局block窝稿,直接返回
return aBlock;
}
else {//為棧block 或者 堆block楣富,由于堆區(qū)需要申請(qǐng)內(nèi)存,所以只可能是棧區(qū)
// Its a stack block. Make a copy. 它是一個(gè)堆棧塊block伴榔,拷貝纹蝴。
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size);//申請(qǐng)空間并接收
if (!result) return NULL;
//通過memmove內(nèi)存拷貝,將 aBlock 拷貝至result
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->invoke;//可以直接調(diào)起invoke
#endif
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING); // XXX not needed 告知可釋放
result->flags |= BLOCK_NEEDS_FREE | 2; // logical refcount 1
_Block_call_copy_helper(result, aBlock);
// Set isa last so memory analysis tools see a fully-initialized object.
result->isa = _NSConcreteMallocBlock;//設(shè)置block對(duì)象類型為堆區(qū)block
return result;
}
}
說明:
- 進(jìn)入_Block_copy源碼踪少,將block 從棧區(qū)拷貝至堆區(qū)
- 如果需要釋放塘安,則直接釋放
- 如果是globalBlock,說明不需要copy秉馏,直接返回
- 反之耙旦,肯定是棧block脱羡,因?yàn)槌跏紕?chuàng)建的不可能是堆block萝究,堆block必須是通過棧block拷貝的
- 此處將棧block拷貝為堆block
- 通過malloc申請(qǐng)內(nèi)存空間用于接收block
- 通過memmove將block拷貝至新申請(qǐng)的內(nèi)存中
- 設(shè)置block對(duì)象的類型為堆區(qū)block,即result->isa = _NSConcreteMallocBlock
4.2 _Block_object_assign 分析
如果block使用外部的局部變量锉罐,則會(huì)開始接下來的兩層拷貝
首先需要知道外部變量的種類有哪些帆竹,下面這些其中用的最多的是BLOCK_FIELD_IS_OBJECT和BLOCK_FIELD_IS_BYREF
// Block 捕獲的外界變量的種類
// Runtime support functions used by compiler when generating copy/dispose helpers
// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
// see function implementation for a more complete description of these fields and combinations
//普通對(duì)象,即沒有其他的引用類型
BLOCK_FIELD_IS_OBJECT = 3, // id, NSObject, __attribute__((NSObject)), block, ...
//block類型作為變量
BLOCK_FIELD_IS_BLOCK = 7, // a block variable
//經(jīng)過__block修飾的變量
BLOCK_FIELD_IS_BYREF = 8, // the on stack structure holding the __block variable
//weak 弱引用變量
BLOCK_FIELD_IS_WEAK = 16, // declared __weak, only used in byref copy helpers
//返回的調(diào)用對(duì)象 - 處理block_byref內(nèi)部對(duì)象內(nèi)存會(huì)加的一個(gè)額外標(biāo)記脓规,配合flags一起使用
BLOCK_BYREF_CALLER = 128, // called from __block (byref) copy/dispose support routines.
};
4.2.1 _Block_object_assign源碼
// __block 變量
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
//普通的對(duì)象類型
case BLOCK_FIELD_IS_OBJECT:
/*******
id object = ...;
[^{ object; } copy];
********/
// objc 指針地址 weakSelf (self)
// arc
_Block_retain_object(object);
// 持有
//持有了當(dāng)前指針栽连,就在這里,強(qiáng)引用
*dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
/*******
void (^object)(void) = ...;
[^{ object; } copy];
********/
// block 被一個(gè) block 捕獲
*dest = _Block_copy(object);
break;
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
/*******
// copy the onstack __block container to the heap
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__block ... x;
__weak __block ... x;
[^{ x; } copy];
********/
*dest = _Block_byref_copy(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
/*******
// copy the actual field held in the __block container
// Note this is MRC unretained __block only.
// ARC retained __block is handled by the copy helper directly.
__block id object;
__block void (^object)(void);
[^{ object; } copy];
********/
*dest = object;
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_WEAK:
/*******
// copy the actual field held in the __block container
// Note this __weak is old GC-weak/MRC-unretained.
// ARC-style __weak is handled by the copy helper directly.
__weak __block id object;
__weak __block void (^object)(void);
[^{ object; } copy];
********/
*dest = object;
break;
default:
break;
}
}
說明:
- 如果是普通對(duì)象侨舆,則交給系統(tǒng)arc處理秒紧,并通過*dest = object拷貝對(duì)象指針,即引用計(jì)數(shù)+1挨下,所以如果直接使用外界的對(duì)象熔恢,則該對(duì)象會(huì)引用計(jì)數(shù)+1不能釋放,這也是循環(huán)引用出現(xiàn)的條件
- 如果是block類型的變量臭笆,則通過_Block_copy操作叙淌,將block從棧區(qū)拷貝到堆區(qū)
- 如果是__weak或__block修飾的變量秤掌,調(diào)用_Block_byref_copy函數(shù),開始捕獲變量
4.2.2 進(jìn)入_Block_byref_copy源碼
源碼:
static struct Block_byref *_Block_byref_copy(const void *arg) {
// Block_byref 結(jié)構(gòu)體鹰霍,捕獲變量的結(jié)構(gòu)體
struct Block_byref *src = (struct Block_byref *)arg;
if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
// src points to stack
//1闻鉴、先創(chuàng)建了一個(gè)需要捕獲的變量的結(jié)構(gòu)體
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
copy->isa = NULL;
// byref value 4 is logical refcount of 2: one for caller, one for stack
copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
//2、在這里將這個(gè)結(jié)構(gòu)體的指針都指向這兩個(gè)變量中茂洒,這樣他們兩個(gè)的修改都會(huì)改變這個(gè)內(nèi)存數(shù)據(jù)
copy->forwarding = copy; // patch heap copy to point to itself
src->forwarding = copy; // patch stack to point to heap copy
copy->size = src->size;
//如果有copy能力 孟岛,
if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
// Trust copy helper to copy everything of interest
// If more than one field shows up in a byref block this is wrong XXX
//Block_byref_2是結(jié)構(gòu)體,__block修飾的可能是對(duì)象督勺,對(duì)象通過byref_keep保存蚀苛,在合適的時(shí)機(jī)進(jìn)行調(diào)用
struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;
if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
copy3->layout = src3->layout;
}
//等價(jià)于 __Block_byref_id_object_copy
(*src2->byref_keep)(copy, src);
}
else {
// Bitwise copy.
// This copy includes Block_byref_3, if any.
memmove(copy+1, src+1, src->size - sizeof(*src));
}
}
// already copied to heap
else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}
return src->forwarding;
}
說明:
- 先將傳入的對(duì)象,強(qiáng)轉(zhuǎn)為Block_byref結(jié)構(gòu)體類型對(duì)象
- 再創(chuàng)建一個(gè)需要捕獲的變量的結(jié)構(gòu)體玷氏,并且將原來的數(shù)據(jù)拷貝到這個(gè)結(jié)構(gòu)體中
- 最后讓src和copy兩者的forwarding都指向copy堵未,也就是他們指向同一個(gè)區(qū)域,這樣就可以做到他們兩個(gè)的修改都會(huì)改變這個(gè)內(nèi)存數(shù)據(jù)
總結(jié):
- 最初創(chuàng)建的需要捕獲的變量的結(jié)構(gòu)體是在棧中
- 之后這個(gè)結(jié)構(gòu)體從棧中copy到堆中
- 最后將棧的指針指向堆中的結(jié)構(gòu)體盏触,堆中的結(jié)構(gòu)體的指針指向自己渗蟹。