iOS 捋清楚block - 1

先看看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 *fpstruct __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_0fp (是Block所執(zhí)行的內(nèi)容) ,最終賦值給__block_impl 的FuncPtr,調(diào)用block的時(shí)候,也就是調(diào)用的這個(gè)FuncPtr
    &__TestObj__testTTT_block_desc_0_DATAdesc , 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é)果如下:


image.png

怎么區(qū)分的呢

  • NSGlobalBlock:沒有訪問auto變量就是global
  • NSStackBlock:訪問auto變量就是Stack
  • NSMallocBlockNSStackBlock調(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

image.png

所以我們只需要搞清楚是否訪問了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]);
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));

而訪問全局變量時(shí)


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局部變量:會(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)引用

image.png

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)引用

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 也不會(huì)進(jìn)行強(qiáng)引用

image.png

因此當(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_2Block中根據(jù)偏移獲取Block_descriptor_2結(jié)構(gòu)體,Block_descriptor_2就是copydispose2個(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_assignflags也是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
}
這下看清楚了一個(gè)是調(diào)的objc源碼里的storeStrong,一個(gè)是copyWeak,看網(wǎng)上基本上都是千篇一律的_Block_object_assign根據(jù)不同的flags進(jìn)行處理,而很少看到對(duì)__weak,傳入的flags也是BLOCK_FIELD_IS_OBJECT有所解釋。說明ARC并不是走的_Block_object_assign,這個(gè)可以跑block的運(yùn)行源碼就會(huì)發(fā)現(xiàn),arc_Block_object_assign函數(shù)根本沒走,斷點(diǎn)斷不住,而在MRC下即可以斷住。

寫的有點(diǎn)多了·下一篇再分析__block吧

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 Block機(jī)制 (Very Good) Block技巧與底層解析 http://www.itdecent.cn...
    Kevin_Junbaozi閱讀 4,163評(píng)論 3 48
  • Block初探 blcok的分類 不同關(guān)鍵字修飾的變量auto和static在OC中有個(gè)默認(rèn)的關(guān)鍵字auto,在我...
    浪的出名閱讀 406評(píng)論 0 0
  • 一 Block的實(shí)現(xiàn) 1. 在main函數(shù)中聲明、實(shí)現(xiàn)并調(diào)用一個(gè)block 2. 然后我們通過clang命令將ma...
    TIGER_XXXX閱讀 584評(píng)論 0 0
  • Block詳解 Block在OC中占有很重要的地位,在蘋果各個(gè)底層庫里面也有大量運(yùn)用,所以就很有必要了解它的構(gòu)成、...
    Joolybgo閱讀 2,224評(píng)論 0 4
  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,884評(píng)論 0 23

友情鏈接更多精彩內(nèi)容