先看看block的结构
void (^block)(void) = ^{
NSLog(@"hello block");
};
block();
block转cpp 看看
void (*block)(void) = ((void (*)())&__TestObj__testTTT_block_impl_0((void *)__TestObj__testTTT_block_func_0, &__TestObj__testTTT_block_desc_0_DATA));
//调用block 可以看到即调用__block_impl->FuncPtr 即如下传入的参数__TestObj__testTTT_block_func_0并将block作为参数
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
去掉一些强转 如下 即调用__TestObj__testTTT_block_impl_0构造函数,并传入2个参数
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
两个结构体组成,还有一个构造函数。可以看到这个构造函数有3个参数(void *fp
,struct __TestObj__testTTT_block_desc_0 *desc
,int flags=0
),而且__block_impl
的结构可以看到里面有一个isa
,所以block也是一个oc对象
。 - 再来看我们上面创建一个block的时候
&__TestObj__testTTT_block_impl_0(__TestObj__testTTT_block_func_0, &__TestObj__testTTT_block_desc_0_DATA);
,通过构造函数传了2个参数,__TestObj__testTTT_block_func_0
即fp
(是Block
所执行的内容) ,最终赋值给__block_impl 的FuncPtr
,调用block的时候,也就是调用的这个FuncPtr
。
&__TestObj__testTTT_block_desc_0_DATA
即desc
, block 的大小信息
block的3种常见类型
如上所述,既然block
也是oc对象,那么我们可以通过class
方法来看看其几种类型。
int abc = 10;
NSLog(@"%@",[^{
}class]);
NSLog(@"%@",[^{
NSLog(@"%d",abc);
}class]);
void (^block)(void) = ^{
NSLog(@"%d",abc);
};
NSLog(@"%@",[block class]);
打印结果如下:
怎么区分的呢
- NSGlobalBlock:没有访问
auto
变量就是global
- NSStackBlock:访问
auto
变量就是Stack
- NSMallocBlock:
NSStackBlock
调用了copy
这里分局部变量和全局变量来看
局部变量:auto
变量(自动变量,离开作用域是会被程序自动释放,我们平常申明的局部变量,默认就是auto
),static
变量(内存中仅此一份,程序结束才销毁)
全局变量:整个程序中都可以被访问
如下:访问static局部变量
static int abc = 10;
NSLog(@"%@",[^{
}class]);
NSLog(@"%@",[^{
NSLog(@"%d",abc);
}class]);
void (^block)(void) = ^{
NSLog(@"%d",abc);
};
全部变成了NSGlobalBlock
了,或者访问全局变量,都是NSGlobalBlock
所以我们只需要搞清楚是否访问了auto变量就可以清晰的知道其是什么block了,如上输出的NSMallocBlock,是因为ARC下进行赋值时,进行了copy
如下 栈block->堆block 源码
// Its a stack block. Make a copy.
struct Block_layout *result =
(struct Block_layout *)malloc(aBlock->descriptor->size); //堆区开辟内存
if (!result) return NULL;
memmove(result, aBlock, aBlock->descriptor->size); // 栈block内存拷贝到堆区
#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下编译器会自动根据如下情况自动将栈上的block
复制到堆上
1.手动调用copy
2.Block是函数的返回值
3.Block被强引用,Block被赋值给__strong或者id类型
4.调用系统API入参中含有usingBlcok的方法
NSGlobalBlock进行copy,什么也不会做,NSStackBlock进行copy则复制到堆,NSMallocBlock进行copy,引用计数增加
变量捕获
先看看局部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));
而访问全局变量时
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局部变量:会被捕获,传进去的是abc 即abc的值也就是10,内部的abc也就是10,解释一下无法改变外界auto局部变量的原因,通俗的来说:外界abc的地址存放的10,而传进来的就是这个存放的内容10,内部的abc的地址跟外部的abc是不一样的,这里只是把10存放到内部的abc地址里,那么你修改内部abc存放的值,对外界abc是不影响的,因为操作的都不是一个地址。
static局部变量:也会被捕获,传进去的是&abc 即外界abc的地址,而外界abc的地址存的是10,block内部用int型指针abc来存放外界abc的地址,也就是说内部这个abc指针=&(外界abc),通过*abc来访问即相当于访问abc指针存放的地址(即外界abc的地址)里的内容即10,而这里的修改就是操作的同一个地址。
全局变量:不会被捕获,在func里直接就访问全局变量
对象变量
__NSStackBlock__
和__NSGlobalBlock__
:不持有对象,__NSMallocBlock__
:持有对象
由于ARC
下,只要进行赋值,即会将block
复制到堆,不太好验证,所以这里在MRC
下验证一下
void (^block)(void);
{
TestObj *obj = [TestObj new];
block = ^{
NSLog(@"%@",obj);
};
NSLog(@"%ld",[obj retainCount]);
[obj release];
}
NSLog(@"%@",block);
来看一下栈block
,如下图,release
后出了作用域释放了,走了dealloc
,说明栈block
并没有对obj
进行强引用
将
block
复制到堆再看看
void (^block)(void);
{
TestObj *obj = [TestObj new];
NSLog(@"%ld",[obj retainCount]);
block = [^{
NSLog(@"%@",obj);
}copy];
NSLog(@"%ld",[obj retainCount]);
[obj release];
}
NSLog(@"%@",block);
如下图,obj
的引用计数增加了,release
后也没有释放,说明堆block
对obj
进行了强引用
再看看
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
也不会进行强引用
因此当我们区分了block的类型之后,只需要搞清楚堆block的强/弱引用关系就行了
- 当
block
里访问对象类型,desc结构体
里会多2个函数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
函数,dispose
指向__TestObj__testTTT_block_dispose_2
函数 - 而
__TestObj__testTTT_block_copy_2
调用的_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);
,看名字应该就是做一个强引用了。
从源码里也可以得到证实,如下
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);
发现最终调用的是Block_callbacks_RR
里的retain
,而这个retain
是调的什么呢,在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入口函数后,做了赋值操作,实际调用的也就是objc
源码里的objc_retain
了,也就是进行了retain
操作,进行了强引用。
还剩一个问题,__TestObj__testTTT_block_copy_2
是什么时候调用的呢
当栈block被复制到堆block
的时候,前面的分析忽略了一句代码_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
中根据偏移获取Block_descriptor_2
结构体,Block_descriptor_2
就是copy
和dispose
2个函数,然后(*desc->copy)(result, aBlock);
调用了copy
,也就是desc
结构里的copy
,即__TestObj__testTTT_block_copy_2
了。
所以__TestObj__testTTT_block_copy_2
在栈block
进行copy
到堆上的时候调用,而__TestObj__testTTT_block_copy_2
里调用_Block_object_assign
,_Block_object_assign
里进行了_Block_retain_object
强引用操作
_Block_object_dispose
就不说了,根据不同的类型进行相应的release
操作
- 这里又发现一个问题,从
cpp
代码里看到,如果__weak修饰,传进去的_Block_object_assign
的flags
也是BLOCK_FIELD_IS_OBJECT
,那么岂不是强引用了,但实际效果是弱引用。这里我们转换一下中间代码.ll来看看,将如下代码通过clang -S -emit-llvm main.m
转换 (需要稍微了解一下llvm方面知识)
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
}
虽然不能完全看懂,但大概也能看到重点,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是写入数据,而@"__block_descriptor_40_e8_32o_e5_v8\01?0l"
,@是全局标识,这个里面可以看到2个函数__copy_helper_block_e8_32o
和__destroy_helper_block_e8_32o
,函数里最后确实如前面分析,调用了_Block_object_assign
和_Block_object_dispose
,但是这还是解释不了__weak
。
那么我们再来看看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
}