Block初探
blcok的分類
- 不同關(guān)鍵字修飾的變量
auto和static
在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)
- auto變量的捕獲
- 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_OBJECT和BLOCK_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);
}
}
}






