block _forwarding指针

前言

上面提到__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 _forwarding指针

分析

  • 我们上面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的实现

上一篇:javascript – 使对象不通过引用传递


下一篇:Superset 实现可视化报表发布