一篇文章剖析block底層源碼以及Block.private

關於我的倉庫

  • 這篇文章是我爲面試準備的iOS基礎知識學習中的一篇
  • 我將準備面試中找到的所有學習資料,寫的Demo,寫的博客都放在了這個倉庫裏iOS-Engineer-Interview
  • 歡迎star??
  • 其中的博客在簡書,CSDN都有發佈
  • 博客中提到的相關的代碼Demo可以在倉庫裏相應的文件夾裏找到

前言

  • 本文主要是對於《高級編程》類似於總結的學習筆記
  • 其實這一部分本質上就是根據多個block的源代碼實例,分析其背後真正的實現原理
  • 在這一塊,我會以例子帶入來講,儘可能把每一個block的源碼講清楚
  • 這一塊難就難在它的順序很亂其實,這個知識點會涉及到好幾個別的知識點,這些知識點我就統一寫在後面,大家看到看不懂的地方,看目錄翻後面的就行
  • 每一部分的源代碼都是現場手動生成

準備工作

  • 閱讀《Objective-C 高級編程》中的p.91 ~ 136

最簡單block

//OC代碼
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^blk)(void) = ^{
            
            printf("Block\n");
        };
        
        blk();
    }
    return 0;
}
//經過clang轉換後的C++代碼
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;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself {

            printf("Block\n");
}

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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}
  • 我們把代碼分成幾塊,一塊一塊分析

__block_impl結構體

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
  • 名字中的impl即implementation的縮寫,換句話說這一部分是block的實現部分結構體
  • void *isa:
    • C語言中void * 爲 “不確定類型指針”,void *可以用來聲明指針。
    • 看到isa就會聯想到之前在objc_class結構體,因此我們的block本質上也是一個對象【而且是個類對象】
    • 我們知道實例對象->類對象->元類構成了isa鏈中的一條,而這個__block_impl結構體佔據的是中間類對象的位置
    • 實例對象應該是生成的block變量,個人認爲
    • 因此這裏的isa指針會指向元類,這裏的元類主要是爲了說明這個塊的存儲區域【詳見:Block存儲域&&Block元類】
  • int Flags:
    • 標識符,在實現block的內部操作時會用到
  • int Reserved:
    • 註明今後版本升級所需區域大小Reserved
    • 一般就是填個0
  • void *FuncPtr:
    • 函數指針
    • 實際執行的函數,也就是block中花括號裏面的代碼內容,最後是轉化成一個C語言函數執行的

struct __main_block_impl_0結構體

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;
  }
};
  • 該結構體纔是塊的完整結構
  • struct __block_impl impl:
    • 就是上面的結構體,作爲塊的實現部分
  • struct __main_block_desc_0* Desc:
    • 這裏的desc即description,作爲塊的補充信息
    • 下面分析
  • __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
    • 這是該結構體的構造函數
    • 其中fp是實際執行的C語言函數指針
    • int flags = 0是C++中的缺省參數,表示默認是0
    • 具體內容就是對impl中相應的內容進行賦值,要說明的是impl.isa = &_NSConcreteStackBlock這個參看Block存儲域&&Block元類

static void __main_block_func_0(struct __main_block_impl_0 *__cself)

static void __main_block_func_0(struct __main_block_impl_0 *__cself {

            printf("Block\n");
}
  • 這一塊就是Blcok執行的實際代碼塊,就如我上面所說,它被轉換爲了一個C++函數
  • 它也是上面的fp函數指針指向的內容
  • 這裏要注意的是傳入的這個__cself參數,他其實就是C語言版的self,代表的就是block本身,畢竟其數據類型就是struct __main_block_impl_0
  • 當然從這段代碼看不出來傳入的cself有什麼用,因爲我們的代碼就只輸出一段話,沒有用到捕獲的變量,後面會講到cself到底怎麼用

static struct __main_block_desc_0

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)};
  • size_t reserved:
    • 今後版本升級所需區域大小
    • 一般就填0
  • size_t Block_size:
    • Block大小
  • __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
    • 這就是和我們平時用結構體一樣,在定義完最後寫一個結構體實例變量,變量名就是__main_block_desc_0_DATA
    • 其中reserved爲0,Block_size是sizeof(struct __main_block_impl_0)

主函數【blk實際調用】

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));

        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}
  • 正常人看到這一段都暈了?
  • 我們把所有強制類型轉換去掉,看個正常人看的懂的版本:
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
// 這一段就是通過構造函數構造一個__main_block_impl_0結構體賦值給blk變量
// 翻譯如下
struct __main_block_impl_0 temp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);
struct __main_block_impl_0 blk = &temp;

// 下面是調用block中函數的過程,我們可以看到我們要調用的其實就是FuncPtr這個函數指針指向的函數
// 查看__main_block_func_0的參數,發現就是我們上面研究的cself
// 所以,該調用翻譯如下:
((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
blk->FuncPtr(blk);
  • 上面值得注意的是,在使用構造函數的時候,我們傳入的參數一個是我們的block函數指針,一個是在定義結構體的時候定義的__main_block_desc_0_DATA
  • 好,這就是不涉及截獲自動變量的最簡單Block分析,下面我們來看捕獲自動變量的情況

截獲自動變量的block

  • 這裏我們又需要看一波C++源碼,這裏面會有很多一樣的代碼,我就不做分析了

  • GOGOGO

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        int dmy = 256;
        int val = 10;
        const char  *fmt = "val = %d\n";
        void (^blk)(void) = ^{
            
            printf(fmt, val);
        };
        val = 2;
        fmt = "THESE VALUES WERE CHANGED. val = %d\n";
        blk();
    }
    return 0;
}
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;
  const char *fmt;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself {
  const char *fmt = __cself->fmt; // bound by copy
  int val = __cself->val; // bound by copy
  
	printf(fmt, val);
}

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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        int dmy = 256;
        int val = 10;
        const char *fmt = "val = %d\n";
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val));
        val = 2;
        fmt = "THESE VALUES WERE CHANGED. val = %d\n";
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}

新的__main_block_impl_0

  • 我們會看到__block_impl結構體沒有任何變化,而__main_block_impl_0多了點東西
struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  const char *fmt;
  int val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • 我們的fmt,val這兩個被block截獲的自動變量被放入到該結構體當中,同時構造函數也發生了變化,構造時要給fmt,val賦值
  • 這裏我們就能大概猜出截獲自動變量的原理了,自動變量會被存入block結構體
  • 在這裏也要注意我們等於是使用了一個長得一模一樣,保存在結構體裏的數來進行的賦值操作,所以我們不能對它進行賦值操作,因爲我們操作的只能是我們自己建的數據,而不會是我們真正的變量

新的__main_block_func_0函數

  • 這次就會用到上面說的cself了
static void __main_block_func_0(struct __main_block_impl_0 *__cself {
  const char *fmt = __cself->fmt; // bound by copy
  int val = __cself->val; // bound by copy
  
	printf(fmt, val);
}
  • 這裏在實際調用時,我們還是隻需要傳入一個cself,我們就會看到在函數內部,我們進行操作的拿來printf不是原來的fmt和val,而是通過塊結構體保存的這兩個值

沒有截獲自動變量,而是使用靜態變量,全局變量情況

  • 在前一篇文章,我們瞭解到,對於截獲的自動變量,不能直接修改它的值,而對於靜態變量,全局變量時OK的,我們來看下對於這些變量block是怎麼處理的
int global_val = 10; // 全局變量
static int static_global_val = 20; // 靜態全局變量

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        static int static_val = 30; // 靜態局部變量
        
        void (^myLocalBlock)(void) = ^{
            global_val *= 1;
            static_global_val *= 2;
            static_val *= 3;
            
            printf("static_val = %d, static_global_val = %d, global_val = %d\n",static_val, static_global_val, global_val);
        };
        
        myLocalBlock();
    }
    return 0;
}
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

int global_val = 10;
static int static_global_val = 20;


struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

            global_val *= 1;
            static_global_val *= 2;
            (*static_val) *= 3;

            printf("static_val = %d, static_global_val = %d, global_val = %d\n",(*static_val), static_global_val, global_val);
        }

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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        static int static_val = 30;

        void (*myLocalBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val));

        ((void (*)(__block_impl *))((__block_impl *)myLocalBlock)->FuncPtr)((__block_impl *)myLocalBlock);
    }
    return 0;
}

新的__main_block_impl_0

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int *static_val;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • 我們發現全局變量,靜態全局變量,我們的Block都沒有用結構體去特地保存它
  • 只有對於我們的靜態局部變量會來保存,但這裏要注意,我們使用的不是int static_val,而是int *static_val
  • 也就是說我們使用一個指針來保存的靜態局部變量
  • 它會直接保存該變量的地址,之後的操作也是直接對該值本身進行操作,而不是向之前截獲的那些變量,等於是重新開闢空間進行保存

新的__main_block_func_0函數

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int *static_val = __cself->static_val; // bound by copy

            global_val *= 1;
            static_global_val *= 2;
            (*static_val) *= 3;

            printf("static_val = %d, static_global_val = %d, global_val = %d\n",(*static_val), static_global_val, global_val);
}
  • 這裏我們使用的global_val以及static_global_val都是直接調用,只有static_val是通過指針獲取值,進行修改
  • 那麼這種做法看起來很不錯,爲什麼在截獲自動變量的時候我們不用指針傳值而是要用值傳值呢?
    • 原因在於,我們的靜態變量是存在數據區的,在程序結束前它其實一直都會存在,之所以會被稱爲局部,只是說出了作用域無法調用到它了,並不是說這塊數據不存在了。因此我們只要自己準備好一個指針,保證出了作用域依然能調用到他就行;而對於自動變量,它們真正的問題在於一但出了作用域,直接被釋放了,所以要在結構體裏開闢空間重新存放,進行值傳遞

使用__block修飾符的情況

  • 在前一篇文章,我們瞭解到,對於截獲的自動變量,不能直接修改它的值,而對於靜態變量,全局變量時OK的,我們來看下對於這些變量block是怎麼處理的
  • __block 修飾符類似於 staticautoregister 說明符,它們用於指定將變量值設置到哪個存儲域中。例如auto 表示作爲自動變量存儲在中, static表示作爲靜態變量存儲在數據區中。
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int val = 10;
        void (^blk)(void) = ^{
            val = 1;
            printf("val = %d\n", val);
        };
        blk();
    }
    return 0;
}

struct __Block_byref_val_0 {
 void *__isa;
 __Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_val_0 *val; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__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_val_0 *val = __cself->val; // bound by ref

            (val->__forwarding->val) = 1;
            printf("val = %d\n", (val->__forwarding->val));
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}
  • 當你覺得block的代碼已經夠多的時候,__block源代碼反手給你個超級加倍告訴你什麼纔是多?

__Block_byref_val_0結構體

struct __Block_byref_val_0 {
 void *__isa;
 __Block_byref_val_0 *__forwarding;
 int __flags;
 int __size;
 int val;
};
  • 對於使用__block修飾的變量,不管在塊裏有沒有使用,都會相應的給他生成一個結構體
  • 這裏的isa指針默認都是傳的空,但實際上是對於C語言基礎數據類型會是0,因爲他們不是對象沒有所屬類,而對於對象其實isa指針指向的就是所屬類
  • 但爲什麼看源碼會是全部都賦值爲0呢,因爲OC是一門動態語言,運行的時候纔會確定下來,不放心的話可以通過class方法查看下
  • 關於__forwarding參看__block的拷貝部分
  • flags標誌符位
  • size大小
  • val變量本身

__main_block_impl_0結構體

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_val_0 *val; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags=0) : val(_val->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
  • 這部分值得注意的是,對於我們的__Block_byref_val_0結構體,我們同樣是用一個指針去保存,這麼做的原因是通過__block修飾的變量可能會被不止一個block使用,使用指針可以保證其可以被多個block調用

__main_block_func_0函數

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_val_0 *val = __cself->val; // bound by ref

            (val->__forwarding->val) = 1;
            printf("val = %d\n", (val->__forwarding->val));
}
  • 這裏看到我們用val截獲下來的就是一個__Block_byref_val_0結構體了,對它進行賦值的時候需要通過forwarding指針進行
  • 下面我們先看下主函數

主函數

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10};
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}
  • 主要關注下__Block_byref_val_0結構體的賦值
__Block_byref_val_0 val = {
    (void*)0,
    (__Block_byref_val_0 *)&val, 
    0, 
    sizeof(__Block_byref_val_0), 
    10
};
  • isa爲0上面解釋過了,forwarding爲自身的地址,flags爲0

DE5552A4-9145-44BB-920B-1F4BA1350266

  • 關於copy和dispose函數我們在下面講對象的時候分析

使用__block修飾的OC對象

  • 目前來看,這段應該是最後一段block使用的源代碼了
  • 兄弟們,頂住
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        __block NSObject *obj = [[NSObject alloc] init];
        NSLog(@"1:%@", obj);
        void (^blk)(void) = ^{
            
            obj = [[NSObject alloc] init];
            NSLog(@"2:%@", obj);
        };
        blk();
    }
    return 0;
}
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

// line 109
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);
}

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


            (obj->__forwarding->obj) = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"));
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_fl_lthb7l6d16q33glgfvv7_md00000gn_T_main_de2130_mi_1, (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(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        __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"))};
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_fl_lthb7l6d16q33glgfvv7_md00000gn_T_main_de2130_mi_0, (obj.__forwarding->obj));
        void (*blk)(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 *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}

__Block_byref_obj_0

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;
};
  • 我們結合主函數中給改結構體初始化的語句來研究下初值是怎麼賦的:
// 原文初始化obj
__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"))};

// 精簡後初始化obj
obj = {
  (void*)0,	// isa
  (__Block_byref_obj_0 *)&obj,	// __forwarding
  33554432,	//	__flags
  sizeof(__Block_byref_obj_0),	// __size
  __Block_byref_id_object_copy_131,	// __Block_byref_id_object_copy
  __Block_byref_id_object_dispose_131,	// __Block_byref_id_object_dispose
  ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))	// obj
}
  • 拋去那些我們已經能理解的,來看下一些新東西:

  • flags = 33554432

    • 33554432即二進制的10000000000000000000000000即1 << 25
    • BLOCK_HAS_COPY_DISPOSE = (1 << 25), // compiler 譯:compiler 含有copy_dispose助手【即擁有copy和dispose函數】
    • 這一塊參看:解析Block.private
  • size

    • 沒什麼好說的
  • __Block_byref_id_object_copy

    • 這一塊參看:解析Block.private
  • __Block_byref_id_object_dispose

    • 這一塊參看:解析Block.private
  • C語言結構體中,編譯器很難處理對象的初始化和銷燬操作,所以使用runtime來管理相關內存。

block 循環引用

  • 舉個例子,下面是一個類的init方法,blk_是該類的一個成員變量:
- (id)init {
	self = [super init];
	blk_ = ^{NSLog(@"self = %@", self);};
	return self;
}
  • 初始化這個類的實例時,就會造成循環引用,因爲 Block 語法賦值在了成員變量 blk_中,因此通過 Block 語法生成在棧上的 Block 此時由棧複製到堆,並持有所使用的 self。self 持有 Block,Block 持有 self。這正是循環引用。

  • 注意:**Block 內使用類的成員變量實際截獲的是這個類本身(self)。**對編譯器來說,成員變量只不過是對象結構體的成員變量。所以如何Block是該類的成員變量,截獲該類其他成員變量時,會造成循環引用。

Block存儲域&&Block元類

  • 在__block_impl結構體中,我們的isa指向一共有三種,也就是Block的三個元類
  • _NSConcreteStackBlock_NSConcreteGlobalBlock_NSConcreteMallocBlock
  • 這三個元類本質上是說明該block是存在棧,數據,堆區

FAD217A2-5F67-40CE-8EF0-97B39C5F1E6F

_NSConcreteGlobalBlock

  • 在兩種情況下使用的block會是 _NSConcreteGlobalBlock 類對象
    • 使用block作爲全局變量,由於在使用全局變量的地方不能使用自動變量,所以不存在對自動變量進行截獲的說法;換句話說,該block實例不依賴於執行時的狀態,在整個程序中只需要一個實例
    • 另外就是在Block語法中沒有截獲自動變量
      • 請注意這裏說的是自動變量,如果截獲的是靜態變量或者全局變量,依然是算沒有截獲自動變量的
      • 其實這麼說第一點也是白瞎,在定義全局的地方肯定無法使用全局變量
      • 因此規則就記住凡事沒有截獲自動變量的就是_NSConcreteGlobalBlock就完事了,吵吵啥呢
  • _NSConcreteGlobalBlock類的Block存儲在**『程序的數據區域』**
  • 我的理解就是,對於沒有要截獲自動變量的block,我們不需要依賴於其運行時的狀態【捕獲的變量】,這樣我們就不涉及到block的copy情況,因此是放在數據區
  • 關於Block的copy參看:Block的拷貝情況
  • 此外要注意的是,通過clang編譯出來的isa在第二種情況下會顯示成stackblock,這是因爲OC是一門動態語言,真正的元類還是在運行的情況下確定的,這種情況下可以使用lldb調試器查看

_NSConcreteStackBlock

  • stackBlock使用情況就跟簡單了,上面兩種情況之外的情況都是stackBlock
  • 換句話說,凡是捕獲了自動變量的Block都是stackBlock
  • 對於數據區上的全局Block就算出了作用域也能使用,因爲在數據區,不會被釋放,直到程序結束;在出了作用域後,依然可以使用指針找到這個塊
  • 而對於StackBlock就沒有那麼好的事了,一旦作用域結束,Block就會被廢棄;同樣,__block也配置在棧上當__block也配置在棧上,當__block的作用域結束的時候,__block也會被廢棄

36D368CB-F071-4BB8-A559-EF531F8D2569

_NSConcreteMallocBlock

  • 上面兩個block好像已經把所有情況都包括進去了,那這個_NSConcreteMallocBlock有什麼用呢,其實完全是爲了block的拷貝存在的
  • 不會有任何一個塊一上來就被存在堆區,請牢記這一點!
  • _NSConcreteMallocBlock存在的意義和autorelease一樣,就是爲了能延長block的作用域
  • 我們將block對象和__blcok對象從棧區複製到堆區,這樣就算棧上的block被廢棄了,還是可以使用堆上那一個
  • 可以聯想我們在ARC是如何處理返回值中的__strong的,大概同理
  • 在這裏要思考一個問題:在棧上和堆上同時有一個block的情況下,我們的賦值,修改,廢棄操作應該怎樣管理?
  • 具體使用,參看Block的拷貝情況

609AFF6A-2136-49C8-B912-BFB40A4E496B

Block的拷貝情況

編譯器判斷的Block的自動拷貝

  • 在ARC中,在大多數情況下,我們的編譯器都會適當地進行判斷
  • 在以下三種情況下,編譯器會給我們自動複製:
    • 將Block作爲函數返回值返回時,會自動拷貝
    • 向方法或函數的參數中傳遞 Block時,使用以下兩種方法的情況下,會進行自動拷貝:
      • 屬於Cocoa框架方法且方法名中含有usingBlock等時
      • Grand Central Dispatch(GCD) 的 API,其中大量用到的block也是會進行復制的
    • 將 Block 賦值給類的附有 __strong修飾符的id類型或 Block 類型成員變量時【當然這種情況就是最多的,只要賦值一個block變量就會進行復制】

手動拷貝

  • 通過copy方法來對Block進行手動拷貝,當我們不確定Block是否會被遺棄,需不需要拷貝時,直接使用copy方法就行,不會造成任何問題

Block 不同類的拷貝效果

Block 類 存儲區域 拷貝效果
_NSConcreteStackBlock 棧區 從棧拷貝到堆
_NSConcreteGlobalBlock 程序的數據區域 不做改變
_NSConcreteMallocBlock 堆區 引用計數增加

__block變量的拷貝

  • 首先__block的拷貝還是跟着block走的,在使用__block 變量的 Block 從棧複製到堆上時,__block 變量也會受到如下影響:
__block 變量的配置存儲區域 Block 從棧複製到堆時的影響
棧區 從棧複製到堆,並被 Block 所持有
堆區 被 Block 所持有
  • 一旦到了堆上,也就是按照OC的內存管理機制來進行管理,此時兩者的關係就從block使用__block變成了block持有__block

  • C5C74FD8-D382-4CEE-BB11-BF4FECDB2676

  • 8AA4AE6C-CD09-4AC5-B860-183271BCA1D3

  • 8C276B23-89A3-49B7-9581-80D7FE6E9107

__forwarding永遠都能正確訪問變量

  • 在複製的過程中就會出現這樣一個問題,由於在棧和堆上都會有我們的__block,我們怎麼找到我們需要的那個呢?
  • 這就用到了__forwarding指針,它在沒有複製的時候就是簡單的指向自己,而當進行復制以後,就會指向堆上的那個__block變量

1B559743-50F3-49AE-9A28-50205F9FF0EE

  • 這樣講可能有點抽象,我再來舉個例子,幫助理解:
int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        __block int val = 1;
        val++;
        printf("1. val = %d\n", val);
        void (^blk)(void) = ^{
            val++;
            printf("2. val = %d\n", val);
        };
        val++;
        printf("3. val = %d\n", val);
        blk();
        val++;
        printf("4. val = %d\n", val);
    }
    return 0;
}

// 打印結果爲:
1. val = 2
3. val = 3
2. val = 4
4. val = 5
  • 這裏面第一個val還是在對於在棧上的val進行自加操作,而之後的2 3 4都是對於堆上的val進行的操作了
  • 但不論是棧上還是堆上的val,它們都可以這麼表示:
++(val->__forwarding->val);
  • 第一個val指的是結構體val,之後通forwarding指向正確的當前結構體,堆上有的話就是指向堆上的那個結構體,而第二個val指向的是結構體中的變量val,通過這樣一番操作,我們就無論如何都可以鎖定到正確的變量val進行操作

解析Block.private

  • 在Block.private文件裏面會解釋真·block源碼
  • 包括block的拷貝究竟是怎麼實現的,block的結構爲什麼會變來變去等等就能得到解答
  • 一如block深似海,發現private才知剛下水

Block的定義

// 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
};

#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
};

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};
  • 畫了下其對應的結構:

  • 爲了擁有更好的博客而畫圖.001

    • 在上面的結構裏雖然Block_layout裏只有一個Block_descriptor_1,但實際上會根據設定的flag值增加Block_descriptor_2或者Block_descriptor_3,但是在上面的結構裏雖然Block_layout裏只有一個Block_descriptor_1是一定會有的
  • Block_layout結構體成員含義如下:

    • isa: 所屬類指針,說明block的類型,就是_NSConcreteStackBlock_NSConcreteMallocBlock_NSConcreteGlobalBlock這幾個,說明OC本身也是一個對象。
    • flags: 標誌變量
    enum {
      	BLOCK_DEALLOCATING =      (0x0001),  // runtime
        BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime	用來標識棧Block
        BLOCK_NEEDS_FREE =        (1 << 24), // runtime	用來標識堆
        BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler compiler 含有copy_dispose助手	如果標記爲BLOCK_HAS_COPY_DISPOSE代表含有copy_dispose助手,說明該lay_out擁有Block_descriptor_2
        BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
        BLOCK_IS_GC =             (1 << 27), // runtime
        BLOCK_IS_GLOBAL =         (1 << 28), // compiler 是否爲全局Block
        BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
        BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler	判斷是否有簽名
        BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler	
    };
    
    // 當然,這幾個flag是可以通過與操作組合在一起的
    // 關於簽名這一塊,參看:NSInvocation動態調用Block
    
    • Reserved: 保留變量
    • invoke: block執行時調用的函數指針,block內部的執行代碼都在這個函數
    • descriptor: block的詳細描述,一定包含Block_descriptor_1,在面對不同的情況時,編譯器會修改lay_out結構,按需增加
    • variables: block範圍外的變量,如果block沒有調用任何外部變量,該變量就不存在

__block變量的定義

// Values for Block_byref->flags to describe __block variables
enum {
    // Byref refcount must use the same bits as Block_layout's refcount.
    // BLOCK_DEALLOCATING =      (0x0001),  // runtime
    // BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime

    BLOCK_BYREF_LAYOUT_MASK =       (0xf << 28), // compiler
    BLOCK_BYREF_LAYOUT_EXTENDED =   (  1 << 28), // compiler
    BLOCK_BYREF_LAYOUT_NON_OBJECT = (  2 << 28), // compiler
    BLOCK_BYREF_LAYOUT_STRONG =     (  3 << 28), // compiler
    BLOCK_BYREF_LAYOUT_WEAK =       (  4 << 28), // compiler
    BLOCK_BYREF_LAYOUT_UNRETAINED = (  5 << 28), // compiler

    BLOCK_BYREF_IS_GC =             (  1 << 27), // runtime

    BLOCK_BYREF_HAS_COPY_DISPOSE =  (  1 << 25), // compiler
    BLOCK_BYREF_NEEDS_FREE =        (  1 << 24), // runtime
};

struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};

  • isa

    • 指向其所屬類,初始爲0,運行時動態判斷
  • forwarding

    • 指向自身/堆上的複製
  • flags

    • // Values for Block_byref->flags to describe __block variables
      enum {
          // Byref refcount must use the same bits as Block_layout's refcount.
          // BLOCK_DEALLOCATING =      (0x0001),  // runtime
          // BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
      
          BLOCK_BYREF_LAYOUT_MASK =       (0xf << 28), // compiler
          BLOCK_BYREF_LAYOUT_EXTENDED =   (  1 << 28), // compiler
          BLOCK_BYREF_LAYOUT_NON_OBJECT = (  2 << 28), // compiler
          BLOCK_BYREF_LAYOUT_STRONG =     (  3 << 28), // compiler
          BLOCK_BYREF_LAYOUT_WEAK =       (  4 << 28), // compiler
          BLOCK_BYREF_LAYOUT_UNRETAINED = (  5 << 28), // compiler
      
          BLOCK_BYREF_IS_GC =             (  1 << 27), // runtime
      
          BLOCK_BYREF_HAS_COPY_DISPOSE =  (  1 << 25), // compiler
          BLOCK_BYREF_NEEDS_FREE =        (  1 << 24), // runtime
      };
      
    • 這個結構和上面基本一樣,上面沒有見名也能知意

    • 值得注意的是這三個:

    • BLOCK_BYREF_LAYOUT_EXTENDED =   (  1 << 28), // compiler	含有layout
      BLOCK_BYREF_HAS_COPY_DISPOSE =  (  1 << 25), // compiler	含有copy_dispose函數,在__block捕獲的變量爲對象時就會生成copy_dispose函數用來管理對象內存
      BLOCK_BYREF_NEEDS_FREE =        (  1 << 24), // runtime	判斷是否要釋放
      
  • size

    • 所佔內存大小
  • Block_byref_2、Block_byref_3

    • Block_byref_2和Block_byref_3用來保存附加信息

Block_copy以及Block_release的底層實現

Block_copy

  • Block.h中,Block_copy是如此定義的:
// Create a heap based copy of a Block or simply add a reference to an existing one.
// This must be paired with Block_release to recover memory, even when running
// under Objective-C Garbage Collection.

// 創建塊的基於堆的副本, 或者只需添加對現有塊的引用
// 這必須與Block_release配對才能恢復內存, 即使在運行時
// 在Objective-C垃圾收集機制下
BLOCK_EXPORT void *_Block_copy(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
  • 它的實現部分在runtime.cpp裏面:
// Copy, or bump refcount, of a block.  If really copying, call the copy helper if present.

// 塊的複製或凹凸【???】引用計數。如果真的要複製,請調用複製助手(如果存在)
// 應該是說不要顯式調用, 不過複製助手也是不能顯式調用的
void *_Block_copy(const void *arg) {
    struct Block_layout *aBlock;

    if (!arg) return NULL;
    
    // The following would be better done as a switch statement
  
    // 下面最好使用switch語句
  	// 這是FIXME?
    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.
      	
      	// 這是一個棧上的block,進行拷貝
        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
      
      	// 不需要XXX
        result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
      	
      	// 邏輯引用計數1
        _Block_call_copy_helper(result, aBlock);
        // Set isa last so memory analysis tools see a fully-initialized object.
      
      	// 設置isa指向,以便於分析內存工具能夠看到完全初始化的對象
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}
  • 在詳細分析copy實現過程前,我們先對其用到的子函數進行一些分析:

latching_incr_int()

static int32_t latching_incr_int(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            return BLOCK_REFCOUNT_MASK;
        }
        if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
            return old_value+2;
        }
    }
}
  • latch:佔有;incr:增量寄存器
  • 這一塊就是對block的應用計數+1的操作,我們可以看到顯然block的引用計數是通過flags去管理的
  • 進入一個死循環,如果flags含有BLOCK_REFCOUNT_MASK證明其引用計數達到最大,直接返回,需要三萬多個指針指向,正常情況下不會出現;其餘情況下,使用OSAtomicCompareAndSwapInt函數【在where取值與old_value相同時,將old_value + 2賦給where
  • 這裏值得注意的是,引用計數每次是 + 2的,block的引用計數是以flags的後16位代表的,以2爲單位,每次遞增2,1被BLOCK_DEALLOCATING佔用

詳解Block_copy

  • 先對於傳入的Block_layout結構體進行判斷,如果爲NULL就直接返回,接下來進入if判斷【就是註釋裏說的可以使用switch】

    1. 對於堆上的block【BLOCK_NEEDS_FREE,話說這個宏真是簡單不做作,堆上的數據就是需要釋放的】,進入latching_incr_int函數對其進行引用計數增加操作【flags】
    2. 對於全局變量,不做任何操作,直接返回【也就是說,copy函數會一直調用嘍】
    3. 剩下的情況就是在棧上的block
  • 對於棧上的block進行拷貝:

    1. 先分配一塊和原block一樣大小的內存空間【失敗直接返回】
    2. 使用memmove()函數進行位複製,將原block的每個字節都拷貝過去【memmove用於拷貝字節,如果目標區域和源區域有重疊的話,memmove能夠保證源串在被覆蓋之前將重疊區域的字節拷貝到目標區域中,但複製後源內容會被更改。但是當目標區域與源區域沒有重疊則和memcpy函數功能相同】
    3. 將新Block的引用計數置零。BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING就是0xffff,~(0xffff)就是0x0000【所以咱就不能直接寫一個0000宏麼】,result->flags與0x0000與等就將result->flags的後16位置零。然後將新Block標識爲堆Block並將其引用計數置爲2【此處存疑,在棧上的block之前的引用計數時用來幹嘛的呢?】
    4. 接着調用copy助手,如果有copy函數的話直接調用,沒有的話直接返回
    5. 設置isa指針,指向_NSConcreteMallocBlock
  • 下面我們看一個例子:

NSObject *obj = [[NSObject alloc] init];
void(^aBlock)() = ^{
  
  obj;
};
NSLog(@"%ld", CFGetRetainCount((__bridge void *)obj));
  • 這樣子的打印結果會是3
  • 由於obj時截獲的外部自動變量,所以我們會用結構體指針指向它來進行保存,所以引用計數會加
  • 在講塊賦值給aBlock的時候,由於塊從棧上覆制到了堆上,在堆上的block也會指向obj,導致其引用計數再加一

Block_release

  • Block.h中,Block_release是如此定義的:
// Lose the reference, and if heap based and last reference, recover the memory

// 失去引用,如果是這是基於堆的並且這是最後一個引用,恢復內存
BLOCK_EXPORT void _Block_release(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
  • 它的實現部分在runtime.cpp裏面:
// API entry point to release a copied Block

// 釋放複製塊的API入口點
void _Block_release(const void *arg) {
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    if (!aBlock) return;
    if (aBlock->flags & BLOCK_IS_GLOBAL) return;
    if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;

    if (latching_decr_int_should_deallocate(&aBlock->flags)) {
        _Block_call_dispose_helper(aBlock);
        _Block_destructInstance(aBlock);
        free(aBlock);
    }
}

latching_decr_int_should_deallocate

// return should_deallocate?
static bool latching_decr_int_should_deallocate(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            return false; // latched high
        }
        if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
            return false;   // underflow, latch low
        }
        int32_t new_value = old_value - 2;
        bool result = false;
        if ((old_value & (BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING)) == 2) {
            new_value = old_value - 1;
            result = true;
        }
        if (OSAtomicCompareAndSwapInt(old_value, new_value, where)) {
            return result;
        }
    }
}
  • 對於引用計數過高(0xfffe),或者過低(0) ,返回false
  • 如果引用計數爲2,減1,說明正在釋放,返回true
  • 如果大於2,則將其引用計數-2返回false

詳解Block_release

  • 對於全局Block,非堆Block不進行處理
  • 判斷引用計數需不需要釋放
  • 對於判斷出來爲true的,也就是引用計數2的,調用助手函數中的dispose函數
  • free

_Block_object_assign以及_Block_object_dispose

  • 在block中的助手函數裏,兩個助手函數copy和dispose,他們實際上時調用的_Block_object_assign以及_Block_object_dispose
__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"))};
  • 千山萬水就差一點,沖沖衝!
  • 我們先來了解下其中會用到的枚舉含義:
// 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, ...	OC對象類型
    BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable	另一個block
    BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable	爲一個被__block修飾後生成的結構體
    BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers	被__weak修飾過的弱引用,只在Block_byref管理內部對象內存時使用,也就是__block __weak id 
    BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.	在處理Block_byref內部對象內存的時候會加的一個額外標記,配合上面的枚舉一起使用;
};

enum {
    BLOCK_ALL_COPY_DISPOSE_FLAGS = 
        BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |
        BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER
};

_Block_object_assign

/
// When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
// to do the assignment.
//
  
// blocks或block_byrefs保存對象時,它們的複製例程助手使用此入口點進行賦值。
void _Block_object_assign(void *destArg, const void *object, const int flags) {
    const void **dest = (const void **)destArg;
  	///  destArg爲執行Block_copy()後的block中的對象、block、或者BYREF指針的指針,object爲copy之前的變量指針。
    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];
        ********/
				
        /// 當block捕獲的變量爲另外一個block時執行此步,copy一個新的block並賦值
        *dest = _Block_copy(object);
        break;
    
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      /// 當block捕獲的變量爲__block修飾的變量時會執行此步,執行byref_copy操作
      case BLOCK_FIELD_IS_BYREF:
      /// 當block捕獲的變量爲__block修飾的變量時會執行此步,執行byref_copy操作
        /*******
         // 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;
    }
}

_Block_byref_copy

// A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
/// 已經複製了一個閉包,它的修復例程要求我們修復對共享byref數據的引用
// Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
/// 未被複制的閉包必須仍然有效,因此每個人在取消對轉發ptr的引用後總是訪問變量。
// We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment and return it.
/// 我們詢問我們知道的byref指針是否已經複製到堆中,如果是,則遞增並返回它。
// Otherwise we need to copy it and update the stack forwarding pointer
/// 否則我們需要複製它並更新堆棧轉發指針
/// 機翻看的想吐?
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
      	/// 對於棧上的byref,先分配內存,isa設置爲NULL
        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
      	/// 新的byref的引用計數要設置爲4,標記爲堆,一份爲調用方,一份爲棧所有,所以爲4【還是2爲單位】
        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
      	/// 將當前byref以及malloc的byref的forwarding都指向堆byref,操作堆棧都是同一份東西
        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;
          	// 執行byref的byref_keep函數(即assign函數,只是會加上BLOCK_BYREF_CALLER標誌),管理捕獲的對象內存
            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.
          
          	/// 如果捕獲的是普通變量,就沒有Block_byref_2,copy+1和src+1指向的就是Block_byref_3,執行字節拷貝。
            memmove(copy+1, src+1, src->size - sizeof(*src));
        }
    }
    // already copied to heap
  	/// 如果該byref是存在於堆,則只需要增加其引用計數。
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}

_Block_object_dispose

// When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
// to help dispose of the contents
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

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);
        }
    }
}
  • 首先賦值給forwaring
  • 如果有助手的話執行destrory
  • free

附錄:Block.h文件

/*
 *  Block.h
 *
 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LLVM_LICENSE_HEADER@
 *
 */

#ifndef _Block_H_
#define _Block_H_

#if !defined(BLOCK_EXPORT)
#   if defined(__cplusplus)
#       define BLOCK_EXPORT extern "C" 
#   else
#       define BLOCK_EXPORT extern
#   endif
#endif

#include <Availability.h>
#include <TargetConditionals.h>

#if __cplusplus
extern "C" {
#endif

// Create a heap based copy of a Block or simply add a reference to an existing one.
// 添加塊的基於堆的副本【???】,或者只是添加對於現有塊的引用
// This must be paired with Block_release to recover memory, even when running
// 即使在運行時,它【複製品】也必須要與block_release配對才能恢復內存【也就是引用計數管理】
// under Objective-C Garbage Collection.
// 處在OC垃圾收集機制下
BLOCK_EXPORT void *_Block_copy(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

// Lose the reference, and if heap based and last reference, recover the memory
// 失去引用,並且該引用爲基於堆的引用並且是最後一個引用,恢復內存【dealloc】
BLOCK_EXPORT void _Block_release(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);


// Used by the compiler. Do not call this function yourself.
// 編譯器調用,不要自行調用該函數
BLOCK_EXPORT void _Block_object_assign(void *, const void *, const int)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

// Used by the compiler. Do not call this function yourself.
// 編譯器調用,不要自行調用該函數
BLOCK_EXPORT void _Block_object_dispose(const void *, const int)
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);

// Used by the compiler. Do not call this function yourself.
// 編譯器調用,不要自行調用該函數
BLOCK_EXPORT void * _NSConcreteGlobalBlock[32]
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteStackBlock[32]
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);


#if __cplusplus
}
#endif

// Type correct macros
// 鍵入正確的宏

#define Block_copy(...) ((__typeof(__VA_ARGS__))_Block_copy((const void *)(__VA_ARGS__)))
#define Block_release(...) _Block_release((const void *)(__VA_ARGS__))


#endif

補充知識:extern “C”

  • 這是鏈接規範語法
  • 規定:extern “language string”,說明之後的內容在應該按照指定語言的方式來處理
  • C++編譯器普遍支持"C"和"C++",分別對應C語言和C++語言
  • 更多相關內容關於extern “C”(詳細剖析)

附錄:Block_private.h文件

/*
 * Block_private.h
 *
 * SPI for Blocks
 *
 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LLVM_LICENSE_HEADER@
 *
 */

#ifndef _BLOCK_PRIVATE_H_
#define _BLOCK_PRIVATE_H_

#include <Availability.h>
#include <AvailabilityMacros.h>
#include <TargetConditionals.h>

#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>

#include <Block.h>

#if __has_include(<ptrauth.h>)
#include <ptrauth.h>
#endif

#if __has_feature(ptrauth_calls) &&  __cplusplus < 201103L

// C ptrauth or old C++ ptrauth

#define _Block_set_function_pointer(field, value)                       \
    ((value)                                                            \
     ? ((field) =                                                       \
        (__typeof__(field))                                             \
        ptrauth_auth_and_resign((void*)(value),                         \
                                ptrauth_key_function_pointer, 0,        \
                                ptrauth_key_block_function, &(field)))  \
     : ((field) = 0))

#define _Block_get_function_pointer(field)                              \
    ((field)                                                            \
     ? (__typeof__(field))                                              \
       ptrauth_auth_function((void*)(field),                            \
                             ptrauth_key_block_function, &(field))      \
     : (__typeof__(field))0)

#else

// C++11 ptrauth or no ptrauth

#define _Block_set_function_pointer(field, value)       \
    (field) = (value)

#define _Block_get_function_pointer(field)      \
    (field)

#endif


#if __has_feature(ptrauth_calls)  &&  __cplusplus >= 201103L

// StorageSignedFunctionPointer<Key, Fn> stores a function pointer of type
// Fn but signed with the given ptrauth key and with the address of its
// storage as extra data.
// Function pointers inside block objects are signed this way.
template <typename Fn, ptrauth_key Key>
class StorageSignedFunctionPointer {
    uintptr_t bits;

 public:

    // Authenticate function pointer fn as a C function pointer.
    // Re-sign it with our key and the storage address as extra data.
    // DOES NOT actually write to our storage.
    uintptr_t prepareWrite(Fn fn) const
    {
        if (fn == nullptr) {
            return 0;
        } else {
            return (uintptr_t)
                ptrauth_auth_and_resign(fn, ptrauth_key_function_pointer, 0,
                                        Key, &bits);
        }
    }

    // Authenticate otherBits at otherStorage.
    // Re-sign it with our storage address.
    // DOES NOT actually write to our storage.
    uintptr_t prepareWrite(const StorageSignedFunctionPointer& other) const
    {
        if (other.bits == 0) {
            return 0;
        } else {
            return (uintptr_t)
                ptrauth_auth_and_resign((void*)other.bits, Key, &other.bits,
                                        Key, &bits);
        }
    }

    // Authenticate ptr as if it were stored at our storage address.
    // Re-sign it as a C function pointer.
    // DOES NOT actually read from our storage.
    Fn completeReadFn(uintptr_t ptr) const
    {
        if (ptr == 0) {
            return nullptr;
        } else {
            return ptrauth_auth_function((Fn)ptr, Key, &bits);
        }
    }

    // Authenticate ptr as if it were at our storage address.
    // Return it as a dereferenceable pointer.
    // DOES NOT actually read from our storage.
    void* completeReadRaw(uintptr_t ptr) const
    {
        if (ptr == 0) {
            return nullptr;
        } else {
            return ptrauth_auth_data((void*)ptr, Key, &bits);
        }
    }

    StorageSignedFunctionPointer() { }

    StorageSignedFunctionPointer(Fn value)
        : bits(prepareWrite(value)) { }

    StorageSignedFunctionPointer(const StorageSignedFunctionPointer& value)
        : bits(prepareWrite(value)) { }

    StorageSignedFunctionPointer&
    operator = (Fn rhs) {
        bits = prepareWrite(rhs);
        return *this;
    }

    StorageSignedFunctionPointer&
    operator = (const StorageSignedFunctionPointer& rhs) {
        bits = prepareWrite(rhs);
        return *this;
    }

    operator Fn () const {
        return completeReadFn(bits);
    }

    explicit operator void* () const {
        return completeReadRaw(bits);
    }

    explicit operator bool () const {
        return completeReadRaw(bits) != nullptr;
    }
};

using BlockCopyFunction = StorageSignedFunctionPointer
    <void(*)(void *, const void *),
     ptrauth_key_block_function>;

using BlockDisposeFunction = StorageSignedFunctionPointer
    <void(*)(const void *),
     ptrauth_key_block_function>;

using BlockInvokeFunction = StorageSignedFunctionPointer
    <void(*)(void *, ...),
     ptrauth_key_block_function>;

using BlockByrefKeepFunction = StorageSignedFunctionPointer
    <void(*)(struct Block_byref *, struct Block_byref *),
     ptrauth_key_block_function>;

using BlockByrefDestroyFunction = StorageSignedFunctionPointer
    <void(*)(struct Block_byref *),
     ptrauth_key_block_function>;

// c++11 and ptrauth_calls
#elif !__has_feature(ptrauth_calls)
// not ptrauth_calls

typedef void(*BlockCopyFunction)(void *, const void *);
typedef void(*BlockDisposeFunction)(const void *);
typedef void(*BlockInvokeFunction)(void *, ...);
typedef void(*BlockByrefKeepFunction)(struct Block_byref*, struct Block_byref*);
typedef void(*BlockByrefDestroyFunction)(struct Block_byref *);

#else
// ptrauth_calls but not c++11

typedef uintptr_t BlockCopyFunction;
typedef uintptr_t BlockDisposeFunction;
typedef uintptr_t BlockInvokeFunction;
typedef uintptr_t BlockByrefKeepFunction;
typedef uintptr_t BlockByrefDestroyFunction;

#endif


// 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
};

#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
};

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};


// Values for Block_byref->flags to describe __block variables
enum {
    // Byref refcount must use the same bits as Block_layout's refcount.
    // BLOCK_DEALLOCATING =      (0x0001),  // runtime
    // BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime

    BLOCK_BYREF_LAYOUT_MASK =       (0xf << 28), // compiler
    BLOCK_BYREF_LAYOUT_EXTENDED =   (  1 << 28), // compiler
    BLOCK_BYREF_LAYOUT_NON_OBJECT = (  2 << 28), // compiler
    BLOCK_BYREF_LAYOUT_STRONG =     (  3 << 28), // compiler
    BLOCK_BYREF_LAYOUT_WEAK =       (  4 << 28), // compiler
    BLOCK_BYREF_LAYOUT_UNRETAINED = (  5 << 28), // compiler

    BLOCK_BYREF_IS_GC =             (  1 << 27), // runtime

    BLOCK_BYREF_HAS_COPY_DISPOSE =  (  1 << 25), // compiler
    BLOCK_BYREF_NEEDS_FREE =        (  1 << 24), // runtime
};

struct Block_byref {
    void *isa;
    struct Block_byref *forwarding;
    volatile int32_t flags; // contains ref count
    uint32_t size;
};

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep;
    BlockByrefDestroyFunction byref_destroy;
};

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};


// Extended layout encoding.

// Values for Block_descriptor_3->layout with BLOCK_HAS_EXTENDED_LAYOUT
// and for Block_byref_3->layout with BLOCK_BYREF_LAYOUT_EXTENDED

// If the layout field is less than 0x1000, then it is a compact encoding 
// of the form 0xXYZ: X strong pointers, then Y byref pointers, 
// then Z weak pointers.

// If the layout field is 0x1000 or greater, it points to a 
// string of layout bytes. Each byte is of the form 0xPN.
// Operator P is from the list below. Value N is a parameter for the operator.
// Byte 0x00 terminates the layout; remaining block data is non-pointer bytes.

enum {
    BLOCK_LAYOUT_ESCAPE = 0, // N=0 halt, rest is non-pointer. N!=0 reserved.
    BLOCK_LAYOUT_NON_OBJECT_BYTES = 1,    // N bytes non-objects
    BLOCK_LAYOUT_NON_OBJECT_WORDS = 2,    // N words non-objects
    BLOCK_LAYOUT_STRONG           = 3,    // N words strong pointers
    BLOCK_LAYOUT_BYREF            = 4,    // N words byref pointers
    BLOCK_LAYOUT_WEAK             = 5,    // N words weak pointers
    BLOCK_LAYOUT_UNRETAINED       = 6,    // N words unretained pointers
    BLOCK_LAYOUT_UNKNOWN_WORDS_7  = 7,    // N words, reserved
    BLOCK_LAYOUT_UNKNOWN_WORDS_8  = 8,    // N words, reserved
    BLOCK_LAYOUT_UNKNOWN_WORDS_9  = 9,    // N words, reserved
    BLOCK_LAYOUT_UNKNOWN_WORDS_A  = 0xA,  // N words, reserved
    BLOCK_LAYOUT_UNUSED_B         = 0xB,  // unspecified, reserved
    BLOCK_LAYOUT_UNUSED_C         = 0xC,  // unspecified, reserved
    BLOCK_LAYOUT_UNUSED_D         = 0xD,  // unspecified, reserved
    BLOCK_LAYOUT_UNUSED_E         = 0xE,  // unspecified, reserved
    BLOCK_LAYOUT_UNUSED_F         = 0xF,  // unspecified, reserved
};


// 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.
};

enum {
    BLOCK_ALL_COPY_DISPOSE_FLAGS = 
        BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_BLOCK | BLOCK_FIELD_IS_BYREF |
        BLOCK_FIELD_IS_WEAK | BLOCK_BYREF_CALLER
};


// Function pointer accessors

static inline __typeof__(void (*)(void *, ...))
_Block_get_invoke_fn(struct Block_layout *block)
{
    return (void (*)(void *, ...))_Block_get_function_pointer(block->invoke);
}

static inline void 
_Block_set_invoke_fn(struct Block_layout *block, void (*fn)(void *, ...))
{
    _Block_set_function_pointer(block->invoke, fn);
}


static inline __typeof__(void (*)(void *, const void *))
_Block_get_copy_fn(struct Block_descriptor_2 *desc)
{
    return (void (*)(void *, const void *))_Block_get_function_pointer(desc->copy);
}

static inline void 
_Block_set_copy_fn(struct Block_descriptor_2 *desc,
                   void (*fn)(void *, const void *))
{
    _Block_set_function_pointer(desc->copy, fn);
}


static inline __typeof__(void (*)(const void *))
_Block_get_dispose_fn(struct Block_descriptor_2 *desc)
{
    return (void (*)(const void *))_Block_get_function_pointer(desc->dispose);
}

static inline void 
_Block_set_dispose_fn(struct Block_descriptor_2 *desc,
                      void (*fn)(const void *))
{
    _Block_set_function_pointer(desc->dispose, fn);
}


// Other support functions


// runtime entry to get total size of a closure
BLOCK_EXPORT size_t Block_size(void *aBlock);

// indicates whether block was compiled with compiler that sets the ABI related metadata bits
BLOCK_EXPORT bool _Block_has_signature(void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);

// returns TRUE if return value of block is on the stack, FALSE otherwise
BLOCK_EXPORT bool _Block_use_stret(void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);

// Returns a string describing the block's parameter and return types.
// The encoding scheme is the same as Objective-C @encode.
// Returns NULL for blocks compiled with some compilers.
BLOCK_EXPORT const char * _Block_signature(void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);

// Returns a string describing the block's GC layout.
// This uses the GC skip/scan encoding.
// May return NULL.
BLOCK_EXPORT const char * _Block_layout(void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);

// Returns a string describing the block's layout.
// This uses the "extended layout" form described above.
// May return NULL.
BLOCK_EXPORT const char * _Block_extended_layout(void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_7_0);

// Callable only from the ARR weak subsystem while in exclusion zone
BLOCK_EXPORT bool _Block_tryRetain(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);

// Callable only from the ARR weak subsystem while in exclusion zone
BLOCK_EXPORT bool _Block_isDeallocating(const void *aBlock)
    __OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_4_3);


// the raw data space for runtime classes for blocks
// class+meta used for stack, malloc, and collectable based blocks
BLOCK_EXPORT void * _NSConcreteMallocBlock[32]
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteAutoBlock[32]
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteFinalizingBlock[32]
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
BLOCK_EXPORT void * _NSConcreteWeakBlockVariable[32]
    __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_2);
// declared in Block.h
// BLOCK_EXPORT void * _NSConcreteGlobalBlock[32];
// BLOCK_EXPORT void * _NSConcreteStackBlock[32];


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);


#endif

附錄:runtime.cpp

/*
 * runtime.cpp
 * libclosure
 *
 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
 *
 * @APPLE_LLVM_LICENSE_HEADER@
 */


#include "Block_private.h"
#include <platform/string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <dlfcn.h>
#include <os/assumes.h>
#ifndef os_assumes
#define os_assumes(_x) os_assumes(_x)
#endif
#ifndef os_assert
#define os_assert(_x) os_assert(_x)
#endif

#define memmove _platform_memmove

#if TARGET_OS_WIN32
#define _CRT_SECURE_NO_WARNINGS 1
#include <windows.h>
static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) 
{ 
    // fixme barrier is overkill -- see objc-os.h
    long original = InterlockedCompareExchange(dst, newl, oldl);
    return (original == oldl);
}

static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst) 
{ 
    // fixme barrier is overkill -- see objc-os.h
    int original = InterlockedCompareExchange(dst, newi, oldi);
    return (original == oldi);
}
#else
#define OSAtomicCompareAndSwapLong(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)
#define OSAtomicCompareAndSwapInt(_Old, _New, _Ptr) __sync_bool_compare_and_swap(_Ptr, _Old, _New)
#endif


/*******************************************************************************
Internal Utilities
********************************************************************************/

static int32_t latching_incr_int(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            return BLOCK_REFCOUNT_MASK;
        }
        if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
            return old_value+2;
        }
    }
}

static bool latching_incr_int_not_deallocating(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        if (old_value & BLOCK_DEALLOCATING) {
            // if deallocating we can't do this
            return false;
        }
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            // if latched, we're leaking this block, and we succeed
            return true;
        }
        if (OSAtomicCompareAndSwapInt(old_value, old_value+2, where)) {
            // otherwise, we must store a new retained value without the deallocating bit set
            return true;
        }
    }
}


// return should_deallocate?
static bool latching_decr_int_should_deallocate(volatile int32_t *where) {
    while (1) {
        int32_t old_value = *where;
        if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
            return false; // latched high
        }
        if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
            return false;   // underflow, latch low
        }
        int32_t new_value = old_value - 2;
        bool result = false;
        if ((old_value & (BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING)) == 2) {
            new_value = old_value - 1;
            result = true;
        }
        if (OSAtomicCompareAndSwapInt(old_value, new_value, where)) {
            return result;
        }
    }
}


/**************************************************************************
Framework callback functions and their default implementations.
***************************************************************************/
#if !TARGET_OS_WIN32
#pragma mark Framework Callback Routines
#endif

static void _Block_retain_object_default(const void *ptr __unused) { }

static void _Block_release_object_default(const void *ptr __unused) { }

static void _Block_destructInstance_default(const void *aBlock __unused) {}

static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
static void (*_Block_destructInstance) (const void *aBlock) = _Block_destructInstance_default;


/**************************************************************************
Callback registration from ObjC runtime and CoreFoundation
***************************************************************************/

void _Block_use_RR2(const Block_callbacks_RR *callbacks) {
    _Block_retain_object = callbacks->retain;
    _Block_release_object = callbacks->release;
    _Block_destructInstance = callbacks->destructInstance;
}

/****************************************************************************
Accessors for block descriptor fields
*****************************************************************************/
#if 0
static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
{
    return aBlock->descriptor;
}
#endif

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;
}

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;
}

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 void _Block_call_dispose_helper(struct Block_layout *aBlock)
{
    struct Block_descriptor_2 *desc = _Block_descriptor_2(aBlock);
    if (!desc) return;

    (*desc->dispose)(aBlock);
}

/*******************************************************************************
Internal Support routines for copying
********************************************************************************/

#if !TARGET_OS_WIN32
#pragma mark Copy/Release support
#endif

// Copy, or bump refcount, of a block.  If really copying, call the copy helper if present.
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;
    }
}


// Runtime entry points for maintaining the sharing knowledge of byref data blocks.

// A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
// Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
// We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment and return it.
// Otherwise we need to copy it and update the stack forwarding pointer
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;
}

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);
        }
    }
}


/************************************************************
 *
 * API supporting SPI
 * _Block_copy, _Block_release, and (old) _Block_destroy
 *
 ***********************************************************/

#if !TARGET_OS_WIN32
#pragma mark SPI/API
#endif


// API entry point to release a copied Block
void _Block_release(const void *arg) {
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    if (!aBlock) return;
    if (aBlock->flags & BLOCK_IS_GLOBAL) return;
    if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;

    if (latching_decr_int_should_deallocate(&aBlock->flags)) {
        _Block_call_dispose_helper(aBlock);
        _Block_destructInstance(aBlock);
        free(aBlock);
    }
}

bool _Block_tryRetain(const void *arg) {
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    return latching_incr_int_not_deallocating(&aBlock->flags);
}

bool _Block_isDeallocating(const void *arg) {
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    return (aBlock->flags & BLOCK_DEALLOCATING) != 0;
}


/************************************************************
 *
 * SPI used by other layers
 *
 ***********************************************************/

size_t Block_size(void *aBlock) {
    return ((struct Block_layout *)aBlock)->descriptor->size;
}

bool _Block_use_stret(void *aBlock) {
    struct Block_layout *layout = (struct Block_layout *)aBlock;

    int requiredFlags = BLOCK_HAS_SIGNATURE | BLOCK_USE_STRET;
    return (layout->flags & requiredFlags) == requiredFlags;
}

// Checks for a valid signature, not merely the BLOCK_HAS_SIGNATURE bit.
bool _Block_has_signature(void *aBlock) {
    return _Block_signature(aBlock) ? true : false;
}

const char * _Block_signature(void *aBlock)
{
    struct Block_layout *layout = (struct Block_layout *)aBlock;
    struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
    if (!desc3) return NULL;

    return desc3->signature;
}

const char * _Block_layout(void *aBlock)
{
    // Don't return extended layout to callers expecting old GC layout
    struct Block_layout *layout = (struct Block_layout *)aBlock;
    if (layout->flags & BLOCK_HAS_EXTENDED_LAYOUT) return NULL;

    struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
    if (!desc3) return NULL;

    return desc3->layout;
}

const char * _Block_extended_layout(void *aBlock)
{
    // Don't return old GC layout to callers expecting extended layout
    struct Block_layout *layout = (struct Block_layout *)aBlock;
    if (! (layout->flags & BLOCK_HAS_EXTENDED_LAYOUT)) return NULL;

    struct Block_descriptor_3 *desc3 = _Block_descriptor_3(layout);
    if (!desc3) return NULL;

    // Return empty string (all non-object bytes) instead of NULL 
    // so callers can distinguish "empty layout" from "no layout".
    if (!desc3->layout) return "";
    else return desc3->layout;
}

#if !TARGET_OS_WIN32
#pragma mark Compiler SPI entry points
#endif

    
/*******************************************************

Entry points used by the compiler - the real API!


A Block can reference four different kinds of things that require help when the Block is copied to the heap.
1) C++ stack based objects
2) References to Objective-C objects
3) Other Blocks
4) __block variables

In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers.  The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign.  The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.

The flags parameter of _Block_object_assign and _Block_object_dispose is set to
    * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
    * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
    * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)

So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.

When  a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions.  Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor.  And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.

So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
    __block id                   128+3       (0x83)
    __block (^Block)             128+7       (0x87)
    __weak __block id            128+3+16    (0x93)
    __weak __block (^Block)      128+7+16    (0x97)
        

********************************************************/

//
// When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
// to do the assignment.
//
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;
    }
}

// When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
// to help dispose of the contents
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;
    }
}


// Workaround for <rdar://26015603> dylib with no __DATA segment fails to rebase
__attribute__((used))
static int let_there_be_data = 42;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章