__block的深入研究
-
__block
可以用于解决block内部无法修改auto变量值的问题 -
__block
不能修饰全局变量、静态变量(static)- 编译器会将
__block
变量包装成一个对象
- 编译器会将
调用的是,从__Block_byref_a_0
的指针找到 a
所在的内存,然后修改值
第一层拷贝(block)
block中的第一层拷贝其实已经讲过了——_Block_copy
将block从栈拷贝到堆
第二层拷贝(捕获变量的内存空间)
在函数声明时会传__main_block_desc_0_DATA
结构体,在里面又会去调用__main_block_copy_0
函数,__main_block_copy_0
里面会调用_Block_object_assign
——这就是第二层拷贝的调用入口。
根据flags & BLOCK_ALL_COPY_DISPOSE_FLAGS
进到不同分支来处理捕获到的变量
此时捕获到的变量是被__block
修饰的BLOCK_FIELD_IS_BYREF
类型,就会调用*dest = _Block_byref_copy(object);
。
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// 用原目标的大小在堆区生成一个Block_byrefstruct 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 stackcopy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;// 原来的区域和新的区域都指向同一个对象,使得block具备了修改能力copy->forwarding = copy; // patch heap copy to point to itselfsrc->forwarding = copy; // patch stack to point to heap copycopy->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 XXXstruct 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 heapelse if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {latching_incr_int(&src->forwarding->flags);}return src->forwarding;
}
用原目标name的大小在堆区生成一个Block_byref
copy->forwarding = copy; & src->forwarding = copy;——原来的区域和新的区域都指向同一个对象,使得block具备了修改能力
(* src2->byref_keep)(copy, src)开始第三层拷贝
第三层拷贝(拷贝对象)
(*src2->byref_keep)(copy, src)
点进去会来到Block_byref
结构来,而byref_keep
是Block_byref
的第5个属性
struct Block_byref {void * __ptrauth_objc_isa_pointer isa;struct Block_byref *forwarding;volatile int32_t flags; // contains ref countuint32_t size;
};struct Block_byref_2 {// requires BLOCK_BYREF_HAS_COPY_DISPOSEBlockByrefKeepFunction byref_keep;BlockByrefDestroyFunction byref_destroy;
};struct Block_byref_3 {// requires BLOCK_BYREF_LAYOUT_EXTENDEDconst char *layout;
};
第5位就等于byref_keep
,所以在第二层拷贝时会调用__Block_byref_id_object_copy_131
。
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);
}
这个(char*)dst + 40
看到__Block_byref_name_0
就顿悟了,刚好取得变量name
对象。
struct __Block_byref_name_0 {void *__isa;
__Block_byref_name_0 *__forwarding;int __flags;int __size;void (*__Block_byref_id_object_copy)(void*, void*);void (*__Block_byref_id_object_dispose)(void*);NSString *name;
};
而_Block_object_assign
在对BLOCK_FIELD_IS_OBJECT
情况时会做出如下操作:
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;
block捕获的外接变量由ARC自动管理,捕获到name
进行拷贝
block中有三层拷贝:拷贝block、拷贝捕获变量的内存地址、拷贝对象
_ 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;}
}
// 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);}
}
- 如果是释放对象就什么也不做(自动释放)
- 如果是
__block
修饰,就将指向指回原来的区域并使用free
释放