block底層實(shí)現(xiàn)

最近讀書(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
應(yīng)用程序的內(nèi)存分配

(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 {
        ...
    }
}

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);
    };
__weak弱引用

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);
    };
引入__block變量

以上是我個(gè)人分析,有不合理的地方笼吟,歡迎指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末库物,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子贷帮,更是在濱河造成了極大的恐慌戚揭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撵枢,死亡現(xiàn)場(chǎng)離奇詭異民晒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)锄禽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)潜必,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沃但,你說(shuō)我怎么就攤上這事磁滚。” “怎么了宵晚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵垂攘,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我淤刃,道長(zhǎng)晒他,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任逸贾,我火速辦了婚禮陨仅,結(jié)果婚禮上津滞,老公的妹妹穿的比我還像新娘。我一直安慰自己灼伤,他們只是感情好据沈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著饺蔑,像睡著了一般锌介。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猾警,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天孔祸,我揣著相機(jī)與錄音,去河邊找鬼发皿。 笑死崔慧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的穴墅。 我是一名探鬼主播惶室,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼玄货!你這毒婦竟也來(lái)了皇钞?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤松捉,失蹤者是張志新(化名)和其女友劉穎夹界,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體隘世,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡可柿,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丙者。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片复斥。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖械媒,靈堂內(nèi)的尸體忽然破棺而出目锭,到底是詐尸還是另有隱情,我是刑警寧澤滥沫,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布侣集,位于F島的核電站,受9級(jí)特大地震影響兰绣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜编振,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一缀辩、第九天 我趴在偏房一處隱蔽的房頂上張望臭埋。 院中可真熱鬧,春花似錦臀玄、人聲如沸瓢阴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)荣恐。三九已至,卻和暖如春累贤,著一層夾襖步出監(jiān)牢的瞬間叠穆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工臼膏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留硼被,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓渗磅,卻偏偏與公主長(zhǎng)得像嚷硫,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子始鱼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355