前言
上面提到__forwarding指针指结构体自己,当使用变量的时候,通过结构体找到__forwarding指针,在通过__forwarding指针找到相应的变量 为什么要这么设计呢?
这样设计的目的是为了方便内存管理
_forwarding指针
代码
__block int a = 30;
void(^block1)(void) = ^ {
NSLog(@"%d",a);
};
Clang
// __Block_byref 结构体
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
// 初始化 讲(__Block_byref_a_0 *)&a 赋给__forwarding指针变量
// __forwarding 指向自己
__Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 30};
匿名函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
// 先在__main_block_impl_0结构体中找到 __Block_byref_a_0的地址
__Block_byref_a_0 *a = __cself->a; // bound by ref
// a->__forwarding->a
NSLog((NSString *)&__NSConstantStringImpl__var_folders_zm_558cwfjs099fbm2r8kxg8wt00000gt_T_main_e97802_mi_0,(a->__forwarding->a));
}
栈block栈调用
// 拿到栈中的a
__Block_byref_a_0 *a = __cself->a;
// 栈中__forwarding指针还是指向自己 再去找a
a->__forwarding->a;
堆block调用
// 拿到栈中的a
__Block_byref_a_0 *a = __cself->a;
// __forwarding指针指向堆 a->__forwarding 获取到堆中的a结构体
// (a->__forwarding->a) 修改堆中a结构体的a变量
(a->__forwarding->a) = 13;
图示
分析
-
我们上面block内部拿到的变量实际就是在堆上的。当block进行copy被复制到堆上时,_Block_object_assign函数内会做这一系列操作 会改变 __forwarding 指向
-
当block在栈时,__Block_byref_a_0结构体内的__forwarding指针指向结构体自己
-
当block被复制到堆中时,栈中的__Block_byref_age_0结构体也会被复制到堆中一份,而此时栈中的__Block_byref_a_0结构体中的__forwarding指针指向的就是堆中的__Block_byref_a_0结构体,堆中__Block_byref_a_0结构体内的__forwarding指针依然指向自己
-
通过__forwarding指针巧妙的将修改的变量赋值在堆中的__Block_byref_a_0中
_Block_object_assign源码内部改变了 __forwarding指针指向
_Block_object_assign
部分核心代码
void _Block_object_assign(void *destArg, const void *object, const int flags) {
const void **dest = (const void **)destArg;
case BLOCK_FIELD_IS_BYREF:
*dest = _Block_byref_copy(object); // *dest指向堆上的byref对象地址 最后返回 src->forwarding ,也就是堆上的byref对象
break;
}
static struct Block_byref *_Block_byref_copy(const void *arg) {
// __Block_byref_a_0强转成Block_byref类型
struct Block_byref *src = (struct Block_byref *)arg;
// 堆空间申请同等大小
struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
// isa设置null
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;
// 堆结构体forwarding指针 指向自己
copy->forwarding = copy;
// 栈结构体forwarding指针 指向堆结构体首地址
src->forwarding = copy;
// 设置堆block大小 和 栈block大小一致
copy->size = src->size;
}
作用
- __Block_byref_a_0强转成Block_byref类型
- 堆空间申请同等大小
- isa设置null
- 修改引用计数
- 堆结构体forwarding指针 指向自己
- 栈结构体forwarding指针 指向堆结构体首地址
- 设置堆block大小 和 栈block大小一致
__forwarding 例子
通过下面的例子加深理解
oc代码
struct Car {
// 类似链表 fw指向的数据类型为Car类型 不一定是自己所在的结构
// 在栈中就指向自己 被copy后 指向堆里的那个结构体
struct Car* _forwarding;
NSString *name;
}car, car2;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
struct Car *p = &car;
p->name = @"奔驰";
p->_forwarding = p;
NSLog(@"%p",p->_forwarding);
NSLog(@"%@",p->_forwarding->name);
NSLog(@"------------copy之前----------");
// 模拟copy 假设car2在堆内存
struct Car *copyP = &car2;
copyP->name = @"奔驰";
// 类似源码copy->forwarding = copy 将堆_Block_byref的_forwarding指向自己
copyP->_forwarding = copyP;
// 类似源码src->forwarding = copy; 将栈_Block_byref的_forwarding指向堆
p->_forwarding = copyP;
NSLog(@"%p",p->_forwarding);
NSLog(@"%@",p->_forwarding->name);
}
输出
0x102c44650
奔驰
------------copy之前----------
0x102c44660
奔驰
总结
block官方源码分析可以看我这篇文章: block copy的实现