IOS-Block

Block初探

blcok的分類

  • 不同關(guān)鍵字修飾的變量autostatic
    在OC中有個(gè)默認(rèn)的關(guān)鍵字auto,在我們創(chuàng)建局部變量的時(shí)候,會(huì)默認(rèn)在局部變量前加上auto關(guān)鍵字進(jìn)行修飾。auto關(guān)鍵字的含義就是它所修飾的變量會(huì)自動(dòng)釋放,也表示著它所修飾的變量會(huì)存放到??臻g,系統(tǒng)會(huì)自動(dòng)對其進(jìn)行釋放。
  • __NSGlobalBlock__全局block,block沒有訪問外部變量,訪問全局的變量,訪問靜態(tài)變量的block都是全局blcok
    static int a = 10;
    static NSString *s1 = @"str1";
    void (^globalBlock1)(void) = ^{
        NSLog(@"-----global block");
    };
    void (^globalBlock2)(void) = ^{
        NSLog(@"-----global block:%d",a);
    };
    void (^globalBlock3)(void) = ^{
        NSLog(@"-----global block:%@",s1);
    };
// 打印結(jié)果如下
<__NSGlobalBlock__: 0x1043a7068>
<__NSGlobalBlock__: 0x1043a7088>
<__NSGlobalBlock__: 0x1043a70a8>
  • __NSStackBlock__棧block,block訪問外部auto變量就是棧block
    auto int b = 11;
    auto NSString *s = @"str";
    NSLog(@"%@%@",^{
        NSLog(@"name is:%d",b);
    },^{
        NSLog(@"name is:%@",s);
    });
// 打印結(jié)果如下
<__NSStackBlock__: 0x7ffeeb858c40>
<__NSStackBlock__: 0x7ffeeb858c18>
  • __NSMallocBlock__堆block,棧block進(jìn)行copy操作之后就變?yōu)槎裝lock
    auto int b = 11;
    self.block = ^{
        NSLog(@"name is:%d",b);
    };
    NSLog(@"%@----%@",self.block,
          [^{
            NSLog(@"name is:%d",b);
            } copy]
          );
// 打印結(jié)果如下
<__NSMallocBlock__: 0x60000096af40>
----<__NSMallocBlock__: 0x60000096a4f0>

在ARC環(huán)境下,編譯器會(huì)根據(jù)情況自動(dòng)將棧上的block復(fù)制到堆上,比如以下情況
block作為函數(shù)返回值時(shí)
將block賦值給__strong指針時(shí)
block作為Cocoa API中方法名含有usingBlock的方法參數(shù)時(shí)
block作為GCD API的方法參數(shù)時(shí)

block的變量捕獲(capture)

  • 我們創(chuàng)建一個(gè)帶外部參數(shù)的block,查看對應(yīng)的結(jié)構(gòu)
int c = 15;
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        auto int a = 10;
        static int b = 20;
        NSObject *obj = [[NSObject alloc] init];
        void(^testBlock)(void) = ^{
            NSLog(@"------- block---arg1:%d---arg2:%d---arg3:%d---arg4:%@",a,b,c,obj);
        };
    }
    return 0;
}
  • 查看有外部參數(shù)block的cpp文件,找到main函數(shù),去掉一些強(qiáng)轉(zhuǎn)代碼,得到下面代碼
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        auto int a = 10;
        static int b = 20;
        NSObject *obj = objc_msgSend)((id)(objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
        void(*testBlock)(void) = (&__main_block_impl_0(__main_block_func_0,                                                  &__main_block_desc_0_DATA,                                                                          a,                                                                         &b,                                                                        obj,                                                                570425344));
    }
    return 0;
}
  • 查看__main_block_impl_0的結(jié)構(gòu)
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int a;
  int *b;
  NSObject *obj;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, NSObject *_obj, int flags=0) : a(_a), b(_b), obj(_obj) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
---------------------------------------------------------------------------
struct __block_impl {
  void *isa;  //isa指針,可以看出Block其實(shí)就是一個(gè)OC對象
  int Flags;
  int Reserved;
  void *FuncPtr;  //函數(shù)內(nèi)存地址
};
---------------------------------------------------------------------------
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};
  • 為了保證block內(nèi)部能夠正常訪問外部的變量,block有個(gè)變量捕獲機(jī)制。
    • auto變量的捕獲
      通過對上述代碼的觀察發(fā)現(xiàn)auto變量auto int a在struct __main_block_impl_0是以int a;的方式保存的。
    • static變量的捕獲
      static變量static int b在struct __main_block_impl_0是以int *b;的方式保存的。
    • 對象類型的auto變量
      當(dāng)block內(nèi)部訪問了對象類型的auto變量時(shí)
      如果block是在棧上,將不會(huì)對auto變量產(chǎn)生強(qiáng)引用
      如果block被拷貝到堆上
      會(huì)調(diào)用block內(nèi)部的copy函數(shù)
      copy函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_assign函數(shù)
      _Block_object_assign函數(shù)會(huì)根據(jù)auto變量的修飾符(__strong、 __weak、__unsafe_unretained)做出相應(yīng)的操作,形成強(qiáng)引用(retain)或者弱引用
      如果block從堆上移除
      會(huì)調(diào)用block內(nèi)部的dispose函數(shù)
      dispose函數(shù)內(nèi)部會(huì)調(diào)用_Block_object_dispose函數(shù)
      _Block_object_dispose函數(shù)會(huì)自動(dòng)釋放引用的auto變量(release)
  • block類型的總結(jié)
block的類型 block執(zhí)行的操作
_NSGlobalBlock_ 沒有訪問auto類型的變量
_NSStackBlock_ 訪問了auto類型的變量
_NSMallocBlock_ _NSStackBlock_類型的block執(zhí)行了copy操作

__block修飾的變量

  • 當(dāng)變量被__block修飾之后,在block結(jié)構(gòu)體里面就不再是簡單的值傳遞了,而是會(huì)將變量包裝成一個(gè)__Block_byref_obj_0結(jié)構(gòu)體,其結(jié)構(gòu)如下
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;
};

// 對應(yīng)的值
 __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)objc_getClass("NSObject"), sel_registerName("new"))};

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
 _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}

Block的循環(huán)引用

  • 在使用block的時(shí)候會(huì)遇到以下循環(huán)引用的問題,vc強(qiáng)引用著block,block強(qiáng)引用著vc,導(dǎo)致vc無法釋放,不會(huì)執(zhí)行dealloc。
typedef void(^block)(void);
@interface ViewController ()
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) block block;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.name = @"xq";
    self.block = ^{
        NSLog(@"name is:%@",self.name);
    };
    self.block();
}
- (void)dealloc
{
    NSLog(@"%s",__func__);
}
@end
  • 知道產(chǎn)生了循環(huán)引用的原因,就可以通過一個(gè)弱引用的weakSelf指向self


    image.png
  • 所以我們只需要將上面的代碼修改成如下,dealloc就會(huì)正常執(zhí)行了
    __weak typeof(self) weakSelf = self;
    self.block = ^{
        NSLog(@"name is:%@",weakSelf.name);
    };
    // dealloc執(zhí)行打印
    -[ViewController dealloc]
  • 但是我們將代碼改成下面這樣,又會(huì)出現(xiàn)問題了,由于vc已經(jīng)釋放了,所以再調(diào)用.name就為空
self.block = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"name is:%@",weakSelf.name);
        });
    };
// 打印結(jié)果
-[ViewController dealloc]
 name is:(null)
  • 所以我們需要在block里面對weakSelf再進(jìn)行一次強(qiáng)引用,這就是所謂weak-strong dance
self.block = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;//在arc中默認(rèn)是strong的所以__strong是可以省略的
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"name is:%@",strongSelf.name);
        });
    };
// 打印結(jié)果
name is:xq
-[ViewController dealloc]
  • 其他解決循環(huán)引用的方法
  • __block,這種方法的缺點(diǎn)是需要依賴block的調(diào)用,如果block不調(diào)用還是會(huì)有循環(huán)引用
    __block ViewController *vc = self;
    self.block = ^{
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"name is:%@",vc.name);
            vc = nil;
        });
    };
    self.block();
  • 傳值,通過將self作為參數(shù)傳遞給block
typedef void(^block)(ViewController *);
self.block = ^(ViewController *vc){
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"name is:%@",vc.name);
        });
    };
 self.block(self);

Block的底層原理

clang研究blcok

  • 創(chuàng)建一個(gè)block,通過clang來觀察下它的底層實(shí)現(xiàn)
    NSString *name = @"xq";
    void (^block)(void) = ^{
        NSLog(@"name is %@",name);
    };
    block();
  • clang后得到的代碼如下
void (*block)(void) = ((void (*)())&__ViewController__viewDidLoad_block_impl_0((void *)__ViewController__viewDidLoad_block_func_0, &__ViewController__viewDidLoad_block_desc_0_DATA, name, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
//去掉一些強(qiáng)轉(zhuǎn)
block = __ViewController__viewDidLoad_block_impl_0(__ViewController__viewDidLoad_block_func_0, __ViewController__viewDidLoad_block_desc_0_DATA,name, 570425344)
block-> FuncPtr(block)
  • __ViewController__viewDidLoad_block_impl_0的結(jié)構(gòu)
  struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  NSString *name;
  // 結(jié)構(gòu)體的構(gòu)造函數(shù)
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, NSString *_name, int flags=0) : name(_name) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • __block_impl的結(jié)構(gòu)
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
  • __ViewController__viewDidLoad_block_desc_0的結(jié)構(gòu)
static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
  void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0), __ViewController__viewDidLoad_block_copy_0, __ViewController__viewDidLoad_block_dispose_0};
// 因?yàn)槲覀儾东@的變量是對象,所以需要對它的內(nèi)存進(jìn)行處理
static void __ViewController__viewDidLoad_block_copy_0(struct __ViewController__viewDidLoad_block_impl_0*dst, struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_assign((void*)&dst->name, (void*)src->name, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __ViewController__viewDidLoad_block_dispose_0(struct __ViewController__viewDidLoad_block_impl_0*src) {_Block_object_dispose((void*)src->name, 3/*BLOCK_FIELD_IS_OBJECT*/);}
  • __ViewController__viewDidLoad_block_func_0就是block要執(zhí)行的任務(wù)
static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
  NSString *name = __cself->name; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_13_ztl_wzln5357kbjwzx2pgrrw0000gp_T_ViewController_d580cd_mi_1,name);
    }
  • 總結(jié) 可以看到block底層是一個(gè)結(jié)構(gòu)體,也是一個(gè)對象,通過構(gòu)造函數(shù)將block的任務(wù)當(dāng)做函數(shù)fp傳遞給結(jié)構(gòu)體,再通過使用fp()調(diào)用,這也是外界調(diào)用block要用block()的原因,其本質(zhì)就是函數(shù)的執(zhí)行,所以block又叫匿名函數(shù),對外界變量的捕獲,當(dāng)變量沒有用block修飾的時(shí)候,會(huì)在block內(nèi)部生成一個(gè)同名的變量并將外界變量的值傳遞進(jìn)來,而當(dāng)變量被__block修飾的時(shí)候,發(fā)現(xiàn)對象會(huì)被包裝成__Block_byref_xxx_0的結(jié)構(gòu)體,根據(jù)傳入變量的類型會(huì)有不同的結(jié)構(gòu)
struct __Block_byref_obj_0 {
  void *__isa;
__Block_byref_obj_0 *__forwarding;
 int __flags;
 int __size;
//對象類型會(huì)有這兩個(gè)方法
 void (*__Block_byref_id_object_copy)(void*, void*);
 void (*__Block_byref_id_object_dispose)(void*);
 NSObject *obj;
};

匯編研究block的底層實(shí)現(xiàn)

  • 在blcok的開始處打下斷點(diǎn)


    image.png
  • 進(jìn)到匯編調(diào)試界面


    image.png
  • 加符號斷點(diǎn)objc_retainBlock
    image.png
  • 進(jìn)到符號斷點(diǎn)


    image.png
  • 繼續(xù)往下執(zhí)行


    image.png
  • 最終會(huì)進(jìn)入到libsystem_blocks.dylib_Block_copy
    image.png
  • 通過打印寄存器我們也可以發(fā)現(xiàn)執(zhí)行完_Block_copy之后,block由棧block轉(zhuǎn)換為堆block
  • 所以我們要研究block就要研究libsystem_blocks.dylib,獲取它的源碼

libsystem_blocks.dylib

  • 通過觀察源碼發(fā)現(xiàn),block的底層其實(shí)是一個(gè)Block_layout的結(jié)構(gòu)體其內(nèi)部結(jié)構(gòu)為
struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};
  • isa:指向block的類型
  • flags:標(biāo)識符,按bit位表示一些block的附加信息,類似于isa中的位域,其中flags的種類有以下幾種,我們重點(diǎn)關(guān)注BLOCK_HAS_COPY_DISPOSE 和 BLOCK_HAS_SIGNATURE。 BLOCK_HAS_COPY_DISPOSE 決定是否有 Block_descriptor_2。BLOCK_HAS_SIGNATURE 決定是否有 Block_descriptor_3
// Values for Block_layout->flags to describe block objects
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};
  • reserved:保留信息
  • invoke:是一個(gè)函數(shù)指針,指向block中的代碼
  • descriptor:block的附加信息,比如保留變量數(shù)、block的大小、copy或dispose的輔助函數(shù)指針,block的簽名。有三類
    Block_descriptor_1是必選的
    Block_descriptor_2 和 Block_descriptor_3都是可選的
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved;
    uintptr_t size;
};

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};
  • 上面塊描述器的構(gòu)造函數(shù)如下,其中Block_descriptor_2和Block_descriptor_3都是通過Block_descriptor_1的地址內(nèi)存平移得到的
#if 0
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
    return aBlock->descriptor;
}
#endif

// Block 的描述 : copy 和 dispose 函數(shù)
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;
}

//  Block 的描述 : 簽名相關(guān)
static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
    if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
    uint8_t *desc = (uint8_t *)aBlock->descriptor;
    desc += sizeof(struct Block_descriptor_1);
    if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
        desc += sizeof(struct Block_descriptor_2);
    }
    return (struct Block_descriptor_3 *)desc;
}
  • _Block_copy的實(shí)現(xiàn)
void *_Block_copy(const void *arg) {
    struct Block_layout *aBlock;

    if (!arg) return NULL;
    
    // The following would be better done as a switch statement
    aBlock = (struct Block_layout *)arg;
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }
    else {
        // 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); // bitcopy first
#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;
        return result;
    }
}
  • _Block_copy會(huì)對外界傳入的blcok先做判斷,如果需要釋放或者是全局block直接返回block,需要釋放的會(huì)對flag做一些處理,如果是棧block就會(huì)在堆中開辟一塊空間,將傳進(jìn)來的blcok拷貝到開辟的空間中并把isa指向_NSConcreteMallocBlock_Block_call_copy_helper方法中對傳遞進(jìn)來的參數(shù)做了一些處理
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
}
  • 如果傳入的參數(shù)不是對象類型的也就沒有copy和dispose函數(shù)的就直接返回,否則就進(jìn)入desc->copy方法,也就是外界傳遞過來的__Block_byref_id_object_copy_xx然后再調(diào)用_Block_object_assign對外界傳遞過來的參數(shù)處理
  • 外界傳遞過來的參數(shù)一般分為以下幾種類型,我們重點(diǎn)研究BLOCK_FIELD_IS_OBJECTBLOCK_FIELD_IS_BYREF
//  Block 捕獲的外界變量的種類
// Runtime support functions used by compiler when generating copy/dispose helpers

// Values for _Block_object_assign() and _Block_object_dispose() parameters
enum {
    // see function implementation for a more complete description of these fields and combinations
    BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
    BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable
    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers
    BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.
};
  • _Block_object_assign的實(shí)現(xiàn)
//  Block 捕獲外界變量的操作
void _Block_object_assign(void *destArg, const void *object, const int flags) {
    const void **dest = (const void **)destArg;
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
      case BLOCK_FIELD_IS_OBJECT:
        /*******
        id object = ...;
        [^{ object; } copy];
        ********/

        _Block_retain_object(object);
        *dest = object;
        break;

      case BLOCK_FIELD_IS_BLOCK:
        /*******
        void (^object)(void) = ...;
        [^{ object; } copy];
        ********/

        *dest = _Block_copy(object);
        break;
    
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
        /*******
         // copy the onstack __block container to the heap
         // Note this __weak is old GC-weak/MRC-unretained.
         // ARC-style __weak is handled by the copy helper directly.
         __block ... x;
         __weak __block ... x;
         [^{ x; } copy];
         ********/

        *dest = _Block_byref_copy(object);
        break;
        
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
        /*******
         // copy the actual field held in the __block container
         // Note this is MRC unretained __block only. 
         // ARC retained __block is handled by the copy helper directly.
         __block id object;
         __block void (^object)(void);
         [^{ object; } copy];
         ********/

        *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:
        /*******
         // copy the actual field held in the __block container
         // Note this __weak is old GC-weak/MRC-unretained.
         // ARC-style __weak is handled by the copy helper directly.
         __weak __block id object;
         __weak __block void (^object)(void);
         [^{ object; } copy];
         ********/

        *dest = object;
        break;

      default:
        break;
    }
}
  • 如果是普通對象,則交給系統(tǒng)arc處理,并拷貝對象指針,即引用計(jì)數(shù)+1,所以外界變量不能釋放
  • 如果是block類型的變量,則通過_Block_copy操作,將block從棧區(qū)拷貝到堆區(qū)
  • 如果是__block修飾的變量,調(diào)用_Block_byref_copy函數(shù) 進(jìn)行內(nèi)存拷貝以及常規(guī)處理
static struct Block_byref *_Block_byref_copy(const void *arg) {
    struct Block_byref *src = (struct Block_byref *)arg;

    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // src points to stack
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        // byref value 4 is logical refcount of 2: one for caller, one for stack
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        copy->forwarding = copy; // patch heap copy to point to itself
        src->forwarding = copy;  // patch stack to point to heap copy
        copy->size = src->size;

        if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
            // Trust copy helper to copy everything of interest
            // If more than one field shows up in a byref block this is wrong XXX
            struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
            copy2->byref_keep = src2->byref_keep;
            copy2->byref_destroy = src2->byref_destroy;

            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                copy3->layout = src3->layout;
            }

            (*src2->byref_keep)(copy, src);
        }
        else {
            // Bitwise copy.
            // This copy includes Block_byref_3, if any.
            memmove(copy+1, src+1, src->size - sizeof(*src));
        }
    }
    // already copied to heap
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}
  • 內(nèi)部對Block_byref進(jìn)行處理,如果已經(jīng)在堆中對他遞增并返回,如果沒有將它拷貝到堆中并更新指針指向它

  • _Block_object_dispose就是對對象的銷毀

void _Block_object_dispose(const void *object, const int flags) {
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
        // get rid of the __block data structure held in a Block
        _Block_byref_release(object);
        break;
      case BLOCK_FIELD_IS_BLOCK:
        _Block_release(object);
        break;
      case BLOCK_FIELD_IS_OBJECT:
        _Block_release_object(object);
        break;
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
        break;
      default:
        break;
    }
}
  • _Block_byref_release的實(shí)現(xiàn)
static void _Block_byref_release(const void *arg) {
    struct Block_byref *byref = (struct Block_byref *)arg;

    // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
    byref = byref->forwarding;
    
    if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
        int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
        os_assert(refcount);
        if (latching_decr_int_should_deallocate(&byref->flags)) {
            if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
                (*byref2->byref_destroy)(byref);
            }
            free(byref);
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,852評論 0 23
  • iOS-Block本質(zhì) 參考篇:iOS-Block淺談[http://www.itdecent.cn/p/25a...
    super_2e20閱讀 246評論 0 1
  • 參考篇:iOS-Block淺談 前言:本文簡述Block本質(zhì),如有錯(cuò)誤請留言指正。 第一部分:Block本質(zhì) Q:...
    夢蕊dream閱讀 61,913評論 41 323
  • 前言 上一篇我們講完了block的基礎(chǔ)知識,這一篇我們就來看看block的底層原理。話不多說,我們創(chuàng)建一個(gè)test...
    xxxxxxxx_123閱讀 271評論 0 0
  • 一. 查看block內(nèi)部實(shí)現(xiàn) 1.編寫block代碼void (^DemoBlock)(int, int) = ^...
    李永開閱讀 385評論 0 0

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