先看看block的結(jié)構(gòu)
void (^block)(void) = ^{
NSLog(@"hello block");
};
block();
block轉(zhuǎn)cpp 看看
void (*block)(void) = ((void (*)())&__TestObj__testTTT_block_impl_0((void *)__TestObj__testTTT_block_func_0, &__TestObj__testTTT_block_desc_0_DATA));
//調(diào)用block 可以看到即調(diào)用__block_impl->FuncPtr 即如下傳入的參數(shù)__TestObj__testTTT_block_func_0并將block作為參數(shù)
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
去掉一些強(qiáng)轉(zhuǎn) 如下 即調(diào)用__TestObj__testTTT_block_impl_0構(gòu)造函數(shù),并傳入2個(gè)參數(shù)
void (*block)(void) = &__TestObj__testTTT_block_impl_0(__TestObj__testTTT_block_func_0, &__TestObj__testTTT_block_desc_0_DATA);
struct __TestObj__testTTT_block_impl_0 {
struct __block_impl impl;
struct __TestObj__testTTT_block_desc_0* Desc;
__TestObj__testTTT_block_impl_0(void *fp, struct __TestObj__testTTT_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
static void __TestObj__testTTT_block_func_0(struct __TestObj__testTTT_block_impl_0 *__cself) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_fv_5rcgr23n5933nyr75rtgn_th0000gn_T_TestObj_59bb6b_mi_0);
}
static struct __TestObj__testTTT_block_desc_0 {
size_t reserved;
size_t Block_size;
} __TestObj__testTTT_block_desc_0_DATA = { 0, sizeof(struct __TestObj__testTTT_block_impl_0)};
- 可以看到
__TestObj__testTTT_block_impl_0由__block_impl和__TestObj__testTTT_block_desc_0兩個(gè)結(jié)構(gòu)體組成,還有一個(gè)構(gòu)造函數(shù)??梢钥吹竭@個(gè)構(gòu)造函數(shù)有3個(gè)參數(shù)(void *fp,struct __TestObj__testTTT_block_desc_0 *desc,int flags=0),而且__block_impl的結(jié)構(gòu)可以看到里面有一個(gè)isa,所以block也是一個(gè)oc對(duì)象。 - 再來看我們上面創(chuàng)建一個(gè)block的時(shí)候
&__TestObj__testTTT_block_impl_0(__TestObj__testTTT_block_func_0, &__TestObj__testTTT_block_desc_0_DATA);,通過構(gòu)造函數(shù)傳了2個(gè)參數(shù),__TestObj__testTTT_block_func_0即fp(是Block所執(zhí)行的內(nèi)容) ,最終賦值給__block_impl 的FuncPtr,調(diào)用block的時(shí)候,也就是調(diào)用的這個(gè)FuncPtr。
&__TestObj__testTTT_block_desc_0_DATA即desc, block 的大小信息
block的3種常見類型
如上所述,既然block也是oc對(duì)象,那么我們可以通過class方法來看看其幾種類型。
int abc = 10;
NSLog(@"%@",[^{
}class]);
NSLog(@"%@",[^{
NSLog(@"%d",abc);
}class]);
void (^block)(void) = ^{
NSLog(@"%d",abc);
};
NSLog(@"%@",[block class]);
打印結(jié)果如下:

怎么區(qū)分的呢
-
NSGlobalBlock:沒有訪問
auto變量就是global -
NSStackBlock:訪問
auto變量就是Stack -
NSMallocBlock:
NSStackBlock調(diào)用了copy
這里分局部變量和全局變量來看
局部變量:auto變量(自動(dòng)變量,離開作用域是會(huì)被程序自動(dòng)釋放,我們平常申明的局部變量,默認(rèn)就是auto),static變量(內(nèi)存中僅此一份,程序結(jié)束才銷毀)
全局變量:整個(gè)程序中都可以被訪問
如下:訪問static局部變量
static int abc = 10;
NSLog(@"%@",[^{
}class]);
NSLog(@"%@",[^{
NSLog(@"%d",abc);
}class]);
void (^block)(void) = ^{
NSLog(@"%d",abc);
};
全部變成了NSGlobalBlock了,或者訪問全局變量,都是NSGlobalBlock

所以我們只需要搞清楚是否訪問了auto變量就可以清晰的知道其是什么block了,如上輸出的NSMallocBlock,是因?yàn)锳RC下進(jìn)行賦值時(shí),進(jìn)行了copy
如下 棧block->堆block 源碼
// Its a stack block. Make a copy.
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size); //堆區(qū)開辟內(nèi)存
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // 棧block內(nèi)存拷貝到堆區(qū)
#if __has_feature(ptrauth_calls)
// Resign the invoke pointer as it uses address authentication.
result->invoke = aBlock->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;/// isa指向堆block
ARC下編譯器會(huì)自動(dòng)根據(jù)如下情況自動(dòng)將棧上的block復(fù)制到堆上
1.手動(dòng)調(diào)用copy
2.Block是函數(shù)的返回值
3.Block被強(qiáng)引用,Block被賦值給__strong或者id類型
4.調(diào)用系統(tǒng)API入?yún)⒅泻衭singBlcok的方法
NSGlobalBlock進(jìn)行copy,什么也不會(huì)做,NSStackBlock進(jìn)行copy則復(fù)制到堆,NSMallocBlock進(jìn)行copy,引用計(jì)數(shù)增加
變量捕獲
先看看局部auto變量
int abc = 10;
NSLog(@"%@",[^{
}class]);
NSLog(@"%@",[^{
NSLog(@"%d",abc);
}class]);
void (^block)(void) = ^{
NSLog(@"%d",abc);
};
block();
NSLog(@"%@",[block class]);

NSLog((NSString *)&__NSConstantStringImpl__var_folders_fv_5rcgr23n5933nyr75rtgn_th0000gn_T_TestObj_d480d7_mi_1,((Class (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__TestObj__testTTT_block_impl_1(__TestObj__testTTT_block_func_1, &__TestObj__testTTT_block_desc_1_DATA, abc)), sel_registerName("class")));
void (*block)(void) = ((void (*)())&__TestObj__testTTT_block_impl_2((void *)__TestObj__testTTT_block_func_2, &__TestObj__testTTT_block_desc_2_DATA, abc));
再看看static局部變量,將abc用static修飾一下

NSLog((NSString *)&__NSConstantStringImpl__var_folders_fv_5rcgr23n5933nyr75rtgn_th0000gn_T_TestObj_f9d821_mi_1,((Class (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__TestObj__testTTT_block_impl_1((void *)__TestObj__testTTT_block_func_1, &__TestObj__testTTT_block_desc_1_DATA, &abc)), sel_registerName("class")));
void (*block)(void) = ((void (*)())&__TestObj__testTTT_block_impl_2((void *)__TestObj__testTTT_block_func_2, &__TestObj__testTTT_block_desc_2_DATA, &abc));
而訪問全局變量時(shí)

NSLog((NSString *)&__NSConstantStringImpl__var_folders_fv_5rcgr23n5933nyr75rtgn_th0000gn_T_TestObj_d8ab72_mi_1,((Class (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)())&__TestObj__testTTT_block_impl_1((void *)__TestObj__testTTT_block_func_1, &__TestObj__testTTT_block_desc_1_DATA)), sel_registerName("class")));
void (*block)(void) = ((void (*)())&__TestObj__testTTT_block_impl_2((void *)__TestObj__testTTT_block_func_2, &__TestObj__testTTT_block_desc_2_DATA));
那么從cpp代碼的角度可以看出
auto局部變量:會(huì)被捕獲,傳進(jìn)去的是abc 即abc的值也就是10,內(nèi)部的abc也就是10,解釋一下無法改變外界auto局部變量的原因,通俗的來說:外界abc的地址存放的10,而傳進(jìn)來的就是這個(gè)存放的內(nèi)容10,內(nèi)部的abc的地址跟外部的abc是不一樣的,這里只是把10存放到內(nèi)部的abc地址里,那么你修改內(nèi)部abc存放的值,對(duì)外界abc是不影響的,因?yàn)椴僮鞯亩疾皇且粋€(gè)地址。
static局部變量:也會(huì)被捕獲,傳進(jìn)去的是&abc 即外界abc的地址,而外界abc的地址存的是10,block內(nèi)部用int型指針abc來存放外界abc的地址,也就是說內(nèi)部這個(gè)abc指針=&(外界abc),通過*abc來訪問即相當(dāng)于訪問abc指針存放的地址(即外界abc的地址)里的內(nèi)容即10,而這里的修改就是操作的同一個(gè)地址。
全局變量:不會(huì)被捕獲,在func里直接就訪問全局變量
對(duì)象變量
__NSStackBlock__和__NSGlobalBlock__:不持有對(duì)象,__NSMallocBlock__:持有對(duì)象
由于ARC下,只要進(jìn)行賦值,即會(huì)將block復(fù)制到堆,不太好驗(yàn)證,所以這里在MRC下驗(yàn)證一下
void (^block)(void);
{
TestObj *obj = [TestObj new];
block = ^{
NSLog(@"%@",obj);
};
NSLog(@"%ld",[obj retainCount]);
[obj release];
}
NSLog(@"%@",block);
來看一下棧block,如下圖,release后出了作用域釋放了,走了dealloc,說明棧block并沒有對(duì)obj進(jìn)行強(qiáng)引用

將
block復(fù)制到堆再看看
void (^block)(void);
{
TestObj *obj = [TestObj new];
NSLog(@"%ld",[obj retainCount]);
block = [^{
NSLog(@"%@",obj);
}copy];
NSLog(@"%ld",[obj retainCount]);
[obj release];
}
NSLog(@"%@",block);
如下圖,obj的引用計(jì)數(shù)增加了,release后也沒有釋放,說明堆block對(duì)obj進(jìn)行了強(qiáng)引用

再看看
GlobalBlock
void (^block)(void);
{
static TestObj *obj;
obj = [TestObj new];
obj.age = 10;
NSLog(@"%ld",[obj retainCount]);
block = [^{
NSLog(@"%@",obj);
}copy];
NSLog(@"%ld",[obj retainCount]);
[obj release];
}
NSLog(@"%@",block);
如下圖,GlobalBlock 也不會(huì)進(jìn)行強(qiáng)引用

因此當(dāng)我們區(qū)分了block的類型之后,只需要搞清楚堆block的強(qiáng)/弱引用關(guān)系就行了
- 當(dāng)
block里訪問對(duì)象類型,desc結(jié)構(gòu)體里會(huì)多2個(gè)函數(shù)copy,dispose
static void __TestObj__testTTT_block_copy_2(struct __TestObj__testTTT_block_impl_2*dst, struct __TestObj__testTTT_block_impl_2*src) {_Block_object_assign((void*)&dst->obj, (void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __TestObj__testTTT_block_dispose_2(struct __TestObj__testTTT_block_impl_2*src) {_Block_object_dispose((void*)src->obj, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static struct __TestObj__testTTT_block_desc_2 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __TestObj__testTTT_block_impl_2*, struct __TestObj__testTTT_block_impl_2*);
void (*dispose)(struct __TestObj__testTTT_block_impl_2*);
} __TestObj__testTTT_block_desc_2_DATA = { 0, sizeof(struct __TestObj__testTTT_block_impl_2), __TestObj__testTTT_block_copy_2, __TestObj__testTTT_block_dispose_2};
- 可以看到
copy指向__TestObj__testTTT_block_copy_2函數(shù),dispose指向__TestObj__testTTT_block_dispose_2函數(shù) - 而
__TestObj__testTTT_block_copy_2調(diào)用的_Block_object_assign
看看_Block_object_assign源碼
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
printf("_Block_object_assign");
switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
case BLOCK_FIELD_IS_OBJECT:
_Block_retain_object(object);
*dest = object;
break;
case BLOCK_FIELD_IS_BLOCK:
*dest = _Block_copy(object);
break;
case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
case BLOCK_FIELD_IS_BYREF:
*dest = _Block_byref_copy(object);
break;
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
*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:
*dest = object;
break;
default:
break;
}
}
從cpp的代碼里看,傳入的是BLOCK_FIELD_IS_OBJECT ,那么走的就是_Block_retain_object(object);,看名字應(yīng)該就是做一個(gè)強(qiáng)引用了。
從源碼里也可以得到證實(shí),如下
static void _Block_retain_object_default(const void *ptr __unused) { }
static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
void _Block_use_RR2(const Block_callbacks_RR *callbacks) {
_Block_retain_object = callbacks->retain;
_Block_release_object = callbacks->release;
_Block_destructInstance = callbacks->destructInstance;
}
struct Block_callbacks_RR {
size_t size; // size == sizeof(struct Block_callbacks_RR)
void (*retain)(const void *);
void (*release)(const void *);
void (*destructInstance)(const void *);
};
typedef struct Block_callbacks_RR Block_callbacks_RR;
BLOCK_EXPORT void _Block_use_RR2(const Block_callbacks_RR *callbacks);
發(fā)現(xiàn)最終調(diào)用的是Block_callbacks_RR里的retain,而這個(gè)retain是調(diào)的什么呢,在libdispatch源碼里可以看到。
void
_os_object_init(void)
{
_objc_init();
Block_callbacks_RR callbacks = {
sizeof(Block_callbacks_RR),
(void (*)(const void *))&objc_retain,
(void (*)(const void *))&objc_release,
(void (*)(const void *))&_os_objc_destructInstance
};
_Block_use_RR2(&callbacks);
#if DISPATCH_COCOA_COMPAT
const char *v = getenv("OBJC_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
v = getenv("DISPATCH_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
v = getenv("LIBDISPATCH_DEBUG_MISSING_POOLS");
if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);
#endif
}
可以看到在_objc_init()runtime入口函數(shù)后,做了賦值操作,實(shí)際調(diào)用的也就是objc源碼里的objc_retain了,也就是進(jìn)行了retain操作,進(jìn)行了強(qiáng)引用。
還剩一個(gè)問題,__TestObj__testTTT_block_copy_2是什么時(shí)候調(diào)用的呢
當(dāng)棧block被復(fù)制到堆block的時(shí)候,前面的分析忽略了一句代碼_Block_call_copy_helper(result, aBlock); ,看看源碼
static void _Block_call_copy_helper(void *result, struct Block_layout *aBlock)
{
struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
if (!desc) return;
(*desc->copy)(result, aBlock); // do fixup
}
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;
desc += sizeof(struct Block_descriptor_1);
return (struct Block_descriptor_2 *)desc;
}
struct Block_descriptor_2 {
// requires BLOCK_HAS_COPY_DISPOSE
BlockCopyFunction copy;
BlockDisposeFunction dispose;
};
大致可以看出:_Block_descriptor_2從Block中根據(jù)偏移獲取Block_descriptor_2結(jié)構(gòu)體,Block_descriptor_2就是copy和dispose2個(gè)函數(shù),然后(*desc->copy)(result, aBlock);調(diào)用了copy ,也就是desc結(jié)構(gòu)里的copy,即__TestObj__testTTT_block_copy_2了。
所以__TestObj__testTTT_block_copy_2在棧block進(jìn)行copy到堆上的時(shí)候調(diào)用,而__TestObj__testTTT_block_copy_2里調(diào)用_Block_object_assign,_Block_object_assign里進(jìn)行了_Block_retain_object強(qiáng)引用操作
_Block_object_dispose就不說了,根據(jù)不同的類型進(jìn)行相應(yīng)的release操作
- 這里又發(fā)現(xiàn)一個(gè)問題,從
cpp代碼里看到,如果__weak修飾,傳進(jìn)去的_Block_object_assign的flags也是BLOCK_FIELD_IS_OBJECT,那么豈不是強(qiáng)引用了,但實(shí)際效果是弱引用。這里我們轉(zhuǎn)換一下中間代碼.ll來看看,將如下代碼通過clang -S -emit-llvm main.m轉(zhuǎn)換 (需要稍微了解一下llvm方面知識(shí))
int main(int argc, const char * argv[]) {
NSObject *obj = [NSObject new];
NSLog(@"111");
void (^mallocBlock)(void) = ^void {
NSLog(@" %@",obj);
};
return 0;
}
@"__block_descriptor_40_e8_32o_e5_v8\01?0l" = linkonce_odr hidden unnamed_addr constant { i64, i64, i8*, i8*, i8*, i64 } { i64 0, i64 40, i8* bitcast (void (i8*, i8*)* @__copy_helper_block_e8_32o to i8*), i8* bitcast (void (i8*)* @__destroy_helper_block_e8_32o to i8*), i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.3, i32 0, i32 0), i64 256 }, align 8
define i32 @main(i32 %0, i8** %1) #1 {
%3 = alloca i32, align 4
%4 = alloca i32, align 4
%5 = alloca i8**, align 8
%6 = alloca %0*, align 8
%7 = alloca void ()*, align 8
%8 = alloca <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, align 8
store i32 0, i32* %3, align 4
store i32 %0, i32* %4, align 4
store i8** %1, i8*** %5, align 8
%9 = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_REFERENCES_$_", align 8
%10 = bitcast %struct._class_t* %9 to i8*
%11 = call i8* @objc_opt_new(i8* %10)
%12 = bitcast i8* %11 to %0*
store %0* %12, %0** %6, align 8
notail call void (i8*, ...) @NSLog(i8* bitcast (%struct.__NSConstantString_tag* @_unnamed_cfstring_ to i8*))
%13 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 0
store i8* bitcast (i8** @_NSConcreteStackBlock to i8*), i8** %13, align 8
%14 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 1
store i32 -1040187392, i32* %14, align 8
%15 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 2
store i32 0, i32* %15, align 4
%16 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 3
store i8* bitcast (void (i8*)* @__main_block_invoke to i8*), i8** %16, align 8
%17 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 4
store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i64 }* @"__block_descriptor_40_e8_32o_e5_v8\01?0l" to %struct.__block_descriptor*), %struct.__block_descriptor** %17, align 8
%18 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 5
%19 = load %0*, %0** %6, align 8
store %0* %19, %0** %18, align 8
%20 = bitcast <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8 to void ()*
store void ()* %20, void ()** %7, align 8
ret i32 0
}
define linkonce_odr hidden void @__copy_helper_block_e8_32o(i8* %0, i8* %1) unnamed_addr #4 {
%3 = alloca i8*, align 8
%4 = alloca i8*, align 8
store i8* %0, i8** %3, align 8
store i8* %1, i8** %4, align 8
%5 = load i8*, i8** %4, align 8
%6 = bitcast i8* %5 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%7 = load i8*, i8** %3, align 8
%8 = bitcast i8* %7 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%9 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %6, i32 0, i32 5
%10 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 5
%11 = load %0*, %0** %9, align 8
%12 = bitcast %0* %11 to i8*
%13 = bitcast %0** %10 to i8*
call void @_Block_object_assign(i8* %13, i8* %12, i32 3) #5
ret void
}
define linkonce_odr hidden void @__destroy_helper_block_e8_32o(i8* %0) unnamed_addr #4 {
%2 = alloca i8*, align 8
store i8* %0, i8** %2, align 8
%3 = load i8*, i8** %2, align 8
%4 = bitcast i8* %3 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%5 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %4, i32 0, i32 5
%6 = load %0*, %0** %5, align 8
%7 = bitcast %0* %6 to i8*
call void @_Block_object_dispose(i8* %7, i32 3) #5
ret void
}
雖然不能完全看懂,但大概也能看到重點(diǎn),store %struct.__block_descriptor* bitcast ({ i64, i64, i8*, i8*, i8*, i64 }* @"__block_descriptor_40_e8_32o_e5_v8\01?0l" to %struct.__block_descriptor*), %struct.__block_descriptor** %17, align 8 store是寫入數(shù)據(jù),而@"__block_descriptor_40_e8_32o_e5_v8\01?0l",@是全局標(biāo)識(shí),這個(gè)里面可以看到2個(gè)函數(shù)__copy_helper_block_e8_32o和__destroy_helper_block_e8_32o,函數(shù)里最后確實(shí)如前面分析,調(diào)用了_Block_object_assign和_Block_object_dispose,但是這還是解釋不了__weak。
那么我們?cè)賮砜纯?code>ARC下的生成的.ll,通過命令clang -S -emit-llvm -fobjc-arc main.m 生成。分別看看__weak和__strong。
define linkonce_odr hidden void @__copy_helper_block_e8_32s(i8* %0, i8* %1) unnamed_addr #4 {
%3 = alloca i8*, align 8
%4 = alloca i8*, align 8
store i8* %0, i8** %3, align 8
store i8* %1, i8** %4, align 8
%5 = load i8*, i8** %4, align 8
%6 = bitcast i8* %5 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%7 = load i8*, i8** %3, align 8
%8 = bitcast i8* %7 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%9 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %6, i32 0, i32 5
%10 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 5
%11 = load %0*, %0** %9, align 8
store %0* null, %0** %10, align 8
%12 = bitcast %0** %10 to i8**
%13 = bitcast %0* %11 to i8*
call void @llvm.objc.storeStrong(i8** %12, i8* %13) #5
ret void
}
define linkonce_odr hidden void @__destroy_helper_block_e8_32s(i8* %0) unnamed_addr #4 {
%2 = alloca i8*, align 8
store i8* %0, i8** %2, align 8
%3 = load i8*, i8** %2, align 8
%4 = bitcast i8* %3 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%5 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %4, i32 0, i32 5
%6 = bitcast %0** %5 to i8**
call void @llvm.objc.storeStrong(i8** %6, i8* null) #5
ret void
}
define linkonce_odr hidden void @__copy_helper_block_e8_32w(i8* %0, i8* %1) unnamed_addr #5 {
%3 = alloca i8*, align 8
%4 = alloca i8*, align 8
store i8* %0, i8** %3, align 8
store i8* %1, i8** %4, align 8
%5 = load i8*, i8** %4, align 8
%6 = bitcast i8* %5 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%7 = load i8*, i8** %3, align 8
%8 = bitcast i8* %7 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%9 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %6, i32 0, i32 5
%10 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %8, i32 0, i32 5
%11 = bitcast %0** %10 to i8**
%12 = bitcast %0** %9 to i8**
call void @llvm.objc.copyWeak(i8** %11, i8** %12) #2
ret void
}
define linkonce_odr hidden void @__destroy_helper_block_e8_32w(i8* %0) unnamed_addr #5 {
%2 = alloca i8*, align 8
store i8* %0, i8** %2, align 8
%3 = load i8*, i8** %2, align 8
%4 = bitcast i8* %3 to <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>*
%5 = getelementptr inbounds <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>, <{ i8*, i32, i32, i8*, %struct.__block_descriptor*, %0* }>* %4, i32 0, i32 5
%6 = bitcast %0** %5 to i8**
call void @llvm.objc.destroyWeak(i8** %6) #2
ret void
}