最近讀書(shū)古涧,關(guān)于block的底層實(shí)現(xiàn),有以下思考和總結(jié)
- c++部分的相關(guān)步驟分析寫(xiě)在代碼注釋
一、block是什么
1.首先寫(xiě)一個(gè)簡(jiǎn)單的block
#import <stdio.h>
int main(void) {
void (^block)(void) = ^{
printf("hello world!");
};
block();
return 0;
}
2.將main.m
編譯后 clang -rewite-objc main.m
生成 .cpp
文件
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;
}
/**
3.參數(shù)fp即傳入的參數(shù)__main_block_func_0函數(shù)的地址毅往,賦值給結(jié)構(gòu)體實(shí)例 impl 的屬性 FuncPtr
4.參數(shù)desc即傳入的 &__main_block_desc_0_DATA結(jié)構(gòu)體取地址賦值給了結(jié)構(gòu)體指針 Desc
5.結(jié)構(gòu)體實(shí)例 impl 的 isa 指針存放了 _NSConcreteStackBlock類(lèi)的地址
6._NSConcreteStackBlock是在將block作為OC對(duì)象處理時(shí)十艾,該類(lèi)的信息放置于_NSConcreteStackBlock 中,
由此可見(jiàn)模闲,block的實(shí)質(zhì)是OC對(duì)象
*/
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("hello world!");
}
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(void) {
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
/**
對(duì)應(yīng)代碼
void (^block)(void) = ^{
printf("hello world!");
};
1.等號(hào)左邊 void (*block)(void)是一個(gè)無(wú)參無(wú)返的函數(shù)指針(是一個(gè)指針建瘫,指向函數(shù))
2.等號(hào)右邊 __main_block_impl_0 首先,c++結(jié)構(gòu)體包含自己的屬性尸折,構(gòu)造方法啰脚,成員方法,
所以 &__main_block_impl_0()是對(duì)結(jié)構(gòu)體構(gòu)造函數(shù)取地址实夹,
函數(shù)的參數(shù)是 (void *)__main_block_func_0 和 &__main_block_desc_0_DATA
*/
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
/**
對(duì)應(yīng)代碼 block();
7.前半部分((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)
訪問(wèn)結(jié)構(gòu)體 __block_impl自己的成員變量FuncPtr橄浓,
FuncPtr在步驟3被賦值了 __main_block_func_0(block執(zhí)行代碼塊)函數(shù)的地址
8.后半部分((__block_impl *)block)粒梦,將block自身作為參數(shù)傳遞
*/
return 0;
}
3.引入變量
int main(void) {
int a = 10;
void (^block)(void) = ^{
printf("%d\n",a);
};
block();
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
/**
2.在c++中 a(_a) 表示 _a 這個(gè)形參賦值給 a 這個(gè)實(shí)參
*/
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
/**
3.定義了一個(gè)新的變量,接收結(jié)構(gòu)體成員變量a的值荸实,__cself->a 表示結(jié)構(gòu)體訪問(wèn)自己的屬性匀们。
*/
printf("%d\n",a);
}
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(void) {
int a = 10;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));
/**
1.將變量a的值傳入,并賦值給了結(jié)構(gòu)體成員變量
*/
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
4.__block變量
int main(void) {
__block int a = 10;
void (^block)(void) = ^{
a += 10;
printf("%d\n",a);
};
block();
return 0;
}
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding; //由步驟2得出指向的結(jié)構(gòu)體本身
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) {
/**
4.將傳入的結(jié)構(gòu)體指針的成員變量__forwarding的值(地址)准给,賦值給當(dāng)前結(jié)構(gòu)體成員變量結(jié)構(gòu)體指針a昼蛀,保存了__block變量地址
*/
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a; // bound by ref
/**
5.訪問(wèn)結(jié)構(gòu)體成員變量a,賦值給新的結(jié)構(gòu)體指針
*/
(a->__forwarding->a) += 10;
/**
6.a->__forwarding首先找到結(jié)構(gòu)體指針的指向圆存,a->__forwarding->a獲取到有效成員變量a的值并進(jìn)行修改
*/
printf("%d\n",(a->__forwarding->a));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
/**
7.棧copy到堆時(shí)調(diào)用叼旋,因?yàn)榻Y(jié)構(gòu)體成員變量包含所截獲的變量或者_(dá)_block變量結(jié)構(gòu)體指針,所以copy的時(shí)候也一起copy了
*/
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
/**
8.堆上的block廢棄時(shí)調(diào)用
*/
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(void) {
__attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 10};
/**
1.變量a不再單純的是一個(gè)int類(lèi)型沦辙,變成了__Block_byref_a_0結(jié)構(gòu)體類(lèi)型夫植,并分別傳入的參數(shù)進(jìn)行賦值
2.第二個(gè)參數(shù)(__Block_byref_a_0 *)&a,結(jié)構(gòu)體指針油讯,值是a的地址详民,a又是結(jié)構(gòu)體__Block_byref_a_0,
所以__Block_byref_a_0中的結(jié)構(gòu)體指針__forwarding指向自身
3.C++中陌兑,__attribute__ 表示可以設(shè)置函數(shù)屬性沈跨。
理解一下字面意思大概知道內(nèi)部發(fā)生了什么
byref 是把內(nèi)存地址告訴程序,所以改變的直接就是內(nèi)存中的數(shù)值兔综。按地址傳遞參數(shù)使過(guò)程用變量的內(nèi)存地址去訪問(wèn)實(shí)際變量的內(nèi)容饿凛。結(jié)果,將變量傳遞給過(guò)程時(shí)软驰,通過(guò)過(guò)程可永遠(yuǎn)改變變量值涧窒。
byval 是把內(nèi)存數(shù)值的拷貝給程序,所以改變的只是拷貝锭亏,內(nèi)存原來(lái)的值是不會(huì)改變的纠吴。按值傳遞參數(shù)時(shí),傳遞的只是變量的副本慧瘤。如果過(guò)程改變了這個(gè)值戴已,則所作變動(dòng)只影響副本而不會(huì)影響變量本身。
*/
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
??結(jié)論:
1.block是函數(shù)指針锅减,指向的是block代碼塊編譯時(shí)生成的函數(shù)地址糖儡。執(zhí)行block相當(dāng)于找到指向block的指針。
2.block對(duì)象是結(jié)構(gòu)體
(1)有isa指針(impl->isa)指向自己的類(lèi)(_NSConcreteStackBlock)
(2)有desc結(jié)構(gòu)體描述block的信息
(3)__forwarding((__Block_byref_a_0 *)a -> __forwarding)指向自己或堆上自己的地址
(4)當(dāng)block對(duì)象截獲變量上煤,變量也會(huì)出現(xiàn)在block結(jié)構(gòu)體中(int a)
(5)指向block代碼塊的函數(shù)指針(impl.FuncPtr = fp)休玩。
(6)block結(jié)構(gòu)體的構(gòu)造函數(shù)的參數(shù),包括函數(shù)指針,描述block的結(jié)構(gòu)體拴疤,自動(dòng)截獲的變量(全局變量不用截獲)永部,引用到的__block變量。(__block對(duì)象也會(huì)轉(zhuǎn)變成結(jié)構(gòu)體)
3.block代碼塊在編譯的時(shí)候會(huì)生成一個(gè)函數(shù)呐矾,函數(shù)的參數(shù)是block對(duì)象結(jié)構(gòu)體指針苔埋。執(zhí)行block,相當(dāng)于通過(guò)block的地址找到__block變量結(jié)構(gòu)體指針蜒犯,再找到變量值组橄,進(jìn)行操作。
二罚随、block的類(lèi)
1.block在編譯中玉工,會(huì)被當(dāng)成結(jié)構(gòu)體處理,block實(shí)際結(jié)構(gòu)
structBlock_literal_1{
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
intflags;
intreserved;
void(*invoke)(void *,...); //block執(zhí)行時(shí)調(diào)用的函數(shù)指針淘菩,block定義時(shí)內(nèi)部的執(zhí)行代碼
structBlock_descriptor_1{
unsigned long intreserved; // NULL
unsigned long intsize; // sizeof(struct Block_literal_1)
// optional helper functions
void(*copy_helper)(void *dst, void *src); // IFF (1<<25)
void(*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
2.block的類(lèi)有三種
block的類(lèi) | 設(shè)置對(duì)象的存儲(chǔ)域 |
---|---|
_NSConcreteGlobalBlock | 程序的數(shù)據(jù)區(qū)域(.data區(qū)) |
_NSConcreteStackBlock | 棧 |
_NSConcreteMallocBlock | 堆 |
(1)_NSConcreteGlobalBlock
#import <Foundation/Foundation.h>
void (^block)(void) = ^{
};
int main(void) {
void (^block1)(void) = ^{
};
NSLog(@"%s %p",object_getClassName(block),block);
NSLog(@"%s %p",object_getClassName(block1),block1);
return 0;
}
//輸出
//__NSGlobalBlock__ 0x100001058
//__NSGlobalBlock__ 0x100001098
(2)_NSConcreteStackBlock
當(dāng)block引入變量遵班,變量?jī)?nèi)存地址會(huì)發(fā)生如下變化
#import <Foundation/Foundation.h>
int main(void) {
int a = 10;
void (^block)(void) = ^{
printf("%d\n",a);
};
NSLog(@"%s %p",object_getClassName(block),block);
NSLog(@"%@",^{
printf("%d\n",a);
});
return 0;
}
//輸出
//__NSMallocBlock__ 0x100443c80
//<__NSStackBlock__: 0x7fff5fbff6b0>
(3)_NSConcreteMallocBlock
從上一步可以看出,block在被賦值后潮改,從棧來(lái)到了堆狭郑,這段代碼是從棧copy到堆的過(guò)程
static void *_Block_copy_internal(const void *arg, const int flags) {
struct Block_layout *aBlock;
...
aBlock = (struct Block_layout *)arg;
...
// Its a stack block. Make a copy.
if (!isGC) {
// 申請(qǐng)block的堆內(nèi)存
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *)0;
// 拷貝棧中block到剛申請(qǐng)的堆內(nèi)存中
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 1;
// 改變isa指向_NSConcreteMallocBlock,即堆block類(lèi)型
result->isa = _NSConcreteMallocBlock;
if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
//printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
}
else {
...
}
}
- 看到這里或許會(huì)有疑問(wèn)汇在,為什么不直接分配在堆區(qū)域上翰萨?
這里引入我讀到的一篇博客的解釋
block是默認(rèn)分配在棧上的唯一OC對(duì)象,因?yàn)榫幾g器更傾向于在棧上分配空間糕殉,因?yàn)閳?zhí)行效率較高亩鬼,但是只能是在已知實(shí)際大小的情況下去分配,只有簡(jiǎn)單的值(比如指針)可以被分配在棧上糙麦,block的大小是確定的辛孵,你創(chuàng)建了一個(gè)給定的block就不能修改丛肮, 在整個(gè)執(zhí)行過(guò)程中block不變赡磅, 它是需要快速執(zhí)行的代碼段,因此它是棧的最佳候選宝与。
??結(jié)論:
1.定義在函數(shù)外的block和定義在函數(shù)內(nèi)部且沒(méi)有捕獲自動(dòng)變量的block是全局block焚廊。
2.ARC下并且有變量捕獲的情況下,對(duì)block自動(dòng)執(zhí)行了copy习劫,將block由棧---->copy---->堆
3.copy的過(guò)程中咆瘟,主要實(shí)現(xiàn),通過(guò)memmove函數(shù)將block內(nèi)容進(jìn)行copy诽里,并且將 isa 指針指向了_NSConcreteMallocBlock
三袒餐、block的copy
1.自身的copy
(1)棧Block
棧block拷貝復(fù)制了內(nèi)容,重置了isa指針指向,重置了flags參數(shù)
struct Block_layout *result = malloc(aBlock->descriptor->size);
if (!result) return (void *)0;
memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
// reset refcount
result->flags &= ~(BLOCK_REFCOUNT_MASK); // XXX not needed
result->flags |= BLOCK_NEEDS_FREE | 1;
result->isa = _NSConcreteMallocBlock;
if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
//printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
(*aBlock->descriptor->copy)(result, aBlock); // do fixup
}
return result;
(2)堆Block
改變引用計(jì)數(shù)
if (aBlock->flags & BLOCK_NEEDS_FREE) {
// latches on high
latching_incr_int(&aBlock->flags);
return aBlock;
}
(3)全局Block
直接返回了傳入的block
else if (aBlock->flags & BLOCK_IS_GLOBAL) {
return aBlock;
}
2.__block變量的copy
(1)__block修飾的基本數(shù)據(jù)類(lèi)型變量
會(huì)生成__Block_byref_a_0
結(jié)構(gòu)體灸眼,__block將基本數(shù)據(jù)類(lèi)型包裝成對(duì)象卧檐,并且只在最初block拷貝時(shí)復(fù)制一次,后面的拷貝只會(huì)增加這個(gè)捕獲變量的引用計(jì)數(shù)焰宣。
(2)沒(méi)有用__block修飾的對(duì)象變量
NSObject *obj = [[NSObject alloc] init];
void(^block)(void) = ^{
NSLog(@"%@",obj);
};
block();
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSObject *obj;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSObject *_obj, int flags=0) : obj(_obj) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSObject *obj = __cself->obj; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7_jctrsbb11gb_vzv3gzmszy1h0000gn_T_main_8ef076_mi_0,obj);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(void) {
NSObject *obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, obj, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
并沒(méi)有生成__Block_byref_a_0
結(jié)構(gòu)體霉囚,在_Block_object_assign
中對(duì)應(yīng)的判斷代碼:
else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
_Block_retain_object(object);
_Block_assign((void *)object, destAddr);
}
通過(guò)以上代碼可以看出,對(duì)對(duì)象進(jìn)行了retain匕积,操作了對(duì)象的引用計(jì)數(shù)
(3)用__block修飾的對(duì)象變量
__block NSObject *obj = [[NSObject alloc] init];
void(^block)(void) = ^{
NSLog(@"%@",obj);
};
block();
struct __Block_byref_obj_0 {
void *__isa;
__Block_byref_obj_0 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *obj;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_obj_0 *obj; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_obj_0 *_obj, int flags=0) : obj(_obj->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_obj_0 *obj = __cself->obj; // bound by ref
NSLog((NSString *)&__NSConstantStringImpl__var_folders_f7_jctrsbb11gb_vzv3gzmszy1h0000gn_T_main_ae30ca_mi_0,(obj->__forwarding->obj));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj, (void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(void) {
__attribute__((__blocks__(byref))) __Block_byref_obj_0 obj = {(void*)0,(__Block_byref_obj_0 *)&obj, 33554432, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_obj_0 *)&obj, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
return 0;
}
生成了__Block_byref_obj_0
結(jié)構(gòu)體盈罐,并且結(jié)構(gòu)體中多了兩個(gè)成員函數(shù),void (*__Block_byref_id_object_copy)(void*, void*);
和 void (*__Block_byref_id_object_dispose)(void*);
用來(lái)實(shí)現(xiàn)對(duì)對(duì)象的內(nèi)存管理闪唆。copy/dispose可以理解為retain(copy)/release
四盅粪、block的循環(huán)引用
#import "Person.h"
typedef void(^block)(void);
@implementation Person
{
block _blk;
}
-(void)test
{
_blk = ^{
NSLog(@"%@",self); //??warning - retainCycle
};
}
@end
這段代碼會(huì)報(bào)warning??,因?yàn)樵斐裳h(huán)引用retainCycle
解決
1.__weak一端變?yōu)槿跻茫∕RC下無(wú)效)
__weak typeof(self)wself = self;
_blk = ^{
NSLog(@"%@",wself);
};
2.引入__block變量(ARC下無(wú)效)
當(dāng)block由棧copy到堆悄蕾,若block使用的變量為附有__block說(shuō)明符的id類(lèi)型或?qū)ο箢?lèi)型的自動(dòng)變量湾揽,不會(huì)被retain
這段代碼在ARC下不會(huì)走dealloc方法
__block id obj = self;
_blk = ^{
NSLog(@"%@",obj);
};
以上是我個(gè)人分析,有不合理的地方笼吟,歡迎指正