iOS 捋清楚block - 1

先看看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 *fpstruct __TestObj__testTTT_block_desc_0 *descint 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_0fp (是Block所执行的内容) ,最终赋值给__block_impl 的FuncPtr,调用block的时候,也就是调用的这个FuncPtr
    &__TestObj__testTTT_block_desc_0_DATAdesc , 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]);

打印结果如下:


image.png

怎么区分的呢

  • NSGlobalBlock:没有访问auto变量就是global
  • NSStackBlock:访问auto变量就是Stack
  • NSMallocBlockNSStackBlock调用了copy
    这里分局部变量和全局变量来看
    局部变量:auto变量(自动变量,离开作用域是会被程序自动释放,我们平常申明的局部变量,默认就是auto),static变量(内存中仅此一份,程序结束才销毁)
    全局变量:整个程序中都可以被访问
    如下:访问static局部变量
static int abc = 10;
    NSLog(@"%@",[^{

    }class]);
    NSLog(@"%@",[^{
        NSLog(@"%d",abc);
    }class]);
    
    void (^block)(void) = ^{
        NSLog(@"%d",abc);
    };

全部变成了NSGlobalBlock了,或者访问全局变量,都是NSGlobalBlock

image.png

所以我们只需要搞清楚是否访问了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]);
image.png
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修饰一下


image.png
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));

而访问全局变量时


image.png
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进行强引用

image.png

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后也没有释放,说明堆blockobj进行了强引用

image.png

再看看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 也不会进行强引用

image.png

因此当我们区分了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_2Block中根据偏移获取Block_descriptor_2结构体,Block_descriptor_2就是copydispose2个函数,然后(*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_assignflags也是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
}
这下看清楚了一个是调的objc源码里的storeStrong,一个是copyWeak,看网上基本上都是千篇一律的_Block_object_assign根据不同的flags进行处理,而很少看到对__weak,传入的flags也是BLOCK_FIELD_IS_OBJECT有所解释。说明ARC并不是走的_Block_object_assign,这个可以跑block的运行源码就会发现,arc_Block_object_assign函数根本没走,断点断不住,而在MRC下即可以断住。

写的有点多了·下一篇再分析__block吧

你可能感兴趣的:(iOS 捋清楚block - 1)