block 解析(2)

上一篇讲的是block和截获变量的特性,这里我们来看一下_block变量。引用官方:You can specify that an imported variable be mutable—that is, read-write— by applying the __block storage type modifier. __blockstorage is similar to, but mutually exclusive of, the registerauto, and static storage types for local variables.
通过指定__block存储类型修饰符,可以读写。__block存储是类似的,但相互排斥的,寄存器,自动变量,和局部变量和静态变量类型(后面这句不太理解)。 我们来写个例子:
void main1()
{
    __block char *_para1="a";
    printf("init _para1:%s,%p,%p\n",_para1,_para1,&_para1);
    void(^testBlock)(void)=^{
        printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1);
    };
    _para1="b";
    printf("before _para1:%s,%p,%p\n",_para1,_para1,&_para1);
    testBlock();
    printf("after _para1:%s,%p,%p\n",_para1,_para1,&_para1);
}
执行后输出如下:init _para1:a,0x47f4,0xbfffc9c0before _para1:b,0x4829,0x8da4580exute _para1:b,0x4829,0x8da4580after _para1:b,0x4829,0x8da4580

这里加了__block修饰符。通过日志可以看出,block内部_para1的地址、值和执行前的_para1一样,在block初始化后,对变量_para1的修改,可以同步到block内,block内并不是截获了变量的值。我们可以看下反编译的后的cpp代码:

//block 的实现函数的对象
struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
//block 引用的参数对象(每一个参数生成一个结构体)
struct __Block_byref__para1_0 {
  void *__isa;
__Block_byref__para1_0 *__forwarding;
 int __flags;
 int __size;
 char *_para1;
};
//block对象
struct __main1_block_impl_0 {
  struct __block_impl impl;
  struct __main1_block_desc_0* Desc;
  __Block_byref__para1_0 *_para1; // by ref
  __main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, __Block_byref__para1_0 *__para1, int flags=0) : _para1(__para1->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
//block 的实现
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) {
  __Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref

        printf("exute _para1:%s,%p,%p\n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1));
    }
//其他函数(copy、dispose:为了管理_Block_byref__para1_0 结构体变量的内存)
static void __main1_block_copy_0(struct __main1_block_impl_0*dst, struct __main1_block_impl_0*src) {_Block_object_assign((void*)&dst->_para1, (void*)src->_para1, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main1_block_dispose_0(struct __main1_block_impl_0*src) {_Block_object_dispose((void*)src->_para1, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main1_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main1_block_impl_0*, struct __main1_block_impl_0*);
  void (*dispose)(struct __main1_block_impl_0*);
} __main1_block_desc_0_DATA = { 0, sizeof(struct __main1_block_impl_0), __main1_block_copy_0, __main1_block_dispose_0};
//这是我们的测试函数
void main1()
{
    __attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*)0,(__Block_byref__para1_0 *)&_para1, 0, sizeof(__Block_byref__para1_0), "a"};
    printf("init _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));
    void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, 570425344);
    (_para1.__forwarding->_para1)="b";
    printf("before _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));
    ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
    printf("after _para1:%s,%p,%p\n",(_para1.__forwarding->_para1),(_para1.__forwarding->_para1),&(_para1.__forwarding->_para1));
}

发现和不加__block不太一样,多了一个结构体(_Block_byref__para1_0)



struct __Block_byref__para1_0 {
  void *__isa;
__Block_byref__para1_0 *__forwarding;
 int __flags;
 int __size;
 char *_para1;
};

这个结构体就是用__block 声明的变量,声明__block变量_para1 转换后的代码如下:

__attribute__((__blocks__(byref))) __Block_byref__para1_0 _para1 = {(void*)0,(__Block_byref__para1_0 *)&_para1, 0, sizeof(__Block_byref__para1_0), "a"};

其实就是生成一个_Block_byref__para1_0对象,__forwarding 指针指向变量结构体自己

void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, (__Block_byref__para1_0 *)&_para1, 570425344);

初始化block,传递结构体变量_para1、函数地址、描述信息等参数,block对象的成员 _para1 引用了_para1结构体的地址,这样就可以修改_para1,而且外界对_para1结构体的修改都可以同步到block对象的成员_para1中,修改代码如下:

(_para1.__forwarding->_para1)="b";

直接通过__Block_byref__para1_0 结构体的成员__forwarding(指向自己)取得_para1成员的地址,然后就可以读写,达到了修改变量的目的。

((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);

执行block,依然是通过调用函数指针 FuncPtr 实现,并传递block自身。函数体代码如下:

static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) {
  __Block_byref__para1_0 *_para1 = __cself->_para1; // bound by ref

        printf("exute _para1:%s,%p,%p\n",(_para1->__forwarding->_para1),(_para1->__forwarding->_para1),&(_para1->__forwarding->_para1));
    }

在函数体内,通过block对象的成员_para1(__Block_byref__para1_0的指针,再通过自身的成员__forwarding指针来获取_para1。

我的理解就是指针引用的关系,不知道为什么要加一个__forwarding指针 ,直接去引用自身的成员 _para1难道不行吗?希望大神赐教。

block 解析(2)

时间: 2024-10-11 21:08:11

block 解析(2)的相关文章

block 解析 - block变量

block变量 上一篇 讲的是block静态变量的特性,这里我们来看一下_block变量.引用官方: You can specify that an imported variable be mutable—that is, read-write— by applying the __block storage type modifier. __blockstorage is similar to, but mutually exclusive of, the register, auto, a

block 解析 - 静态变量

静态变量 上一篇 我们了解了block全局变量的使用,静态变量和全局变量一样,可以直接在block内部使用,也可以在block内部修改 引用官方文档: Global variables are accessible, including static variables that exist within the enclosing lexical scope. 我们来看一段代码: 声明一个静态变量,在block内部修改 static NSString * _para1; -(void )tes

block 解析 - 成员变量

成员变量 在 上一篇 中我们讲了截获变量特性,对于局部变量,变量不加__block修饰符,在block内部是无法修改变量的值,而且初始化block之后,对变量修改,就无法同步到block内部,但是对于成员变量,结果却不一样,即时不加__block修饰符,block初始化后,对于block内部引用的变量的修改,也能同步到block内部,并且在block内部可以修改成员变量的值. Demo: 声明两个变量:_person2._p1 @interface KDBlockTest() { NSStrin

block 解析 - 局部变量

局部变量 block内使用局部变量,一般都是截获变量(只读),截获离block初始化最近的一次的值. 引用官方文档: Stack (non-static) variables local to the enclosing lexical scope are captured as const variables.Their values are taken at the point of the block expression within the program. In nested blo

block 解析 - 内存

回顾 上一篇 我们了解到了用__block修饰的变量,可以在block内部修改,__block变量其实对应一个结构体 struct __Block_byref__para1_0 { void *__isa; __Block_byref__para1_0 *__forwarding; int __flags; int __size; char *_para1; }; block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了 struct

block 解析 - 形参变量

block形参 之前漏了一篇block形参的介绍,这里给补上. block形参就是定义block带的参数,和函数的参数使用一样,我们可以在block随意使用修改block形参. 我们来看个例子: 我们声明了两个NSString 指针_p1 _p2.int 型_p3.可变数组_p4,并把这些参数传入block,在block内修改. -(void )test3 { NSString *_p1=[NSString stringWithFormat:@"hello %@",@"wor

iOS开发之block解析

1. block的本质是一个Objective-C的对象.为什么这么说? 在Objective-C中.runtime会在执行时依据对象的isa指针的指向,来度额定这个对象的类型.也能够觉得一个对象.它具有isa指针.就是一个OC对象 2. 你怎么知道block有isa指针呢.我们能够通过clang命令将来看block的实现 //測试代码 int main(int argc, const char * argv[]) { @autoreleasepool { void(^blk)(void)=^{

OC之block解析

1.相关概念 在这篇笔记开始之前,我们需要对以下概念有所了解. 1.1 操作系统中的栈和堆 注:这里所说的堆和栈与数据结构中的堆和栈不是一回事. 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方式类似于数据结构中的栈,即后进先出.先进后出的原则. 例如:在函数中申明一个局部变量int b;系统自动在栈中为b开辟空间. 堆区(heap):一般由程序员申请并指明大小,最终也由

iOS中Block的用法,举例,解析与底层原理(这可能是最详细的Block解析)

[摘要]这篇文章,首先在第1节中介绍Block的定义,以及与C里面函数的对比.然后,第2节介绍实际开发中经常会用到的Block语法形式,以供读者日后查阅.只知道怎么用却不知什么时候用?所以随后的第3节将介绍Block的应用场景.然而,用Block不当导致了Crash?所以,第4节有必要了解Block捕获变量的特性,以及循环引用的解决.另外,千万不要懒,一碰到Block就weak,要区分哪些不会引起循环引用.然而,如果对Block的内存机制不熟悉,也会导致Crash,所以第5节会介绍Block的内