block 解析 - 局部变量

局部变量

block内使用局部变量,一般都是截获变量(只读),截获离block初始化最近的一次的值。

引用官方文档:

  1. 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 blocks, the value is captured from the nearest enclosing scope. 

我们做一个测试,了解一下原理

代码如下:

void main1()
{
    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,0x97f70,0x27d70988
before _para1:b,0x97fa5,0x27d70988
exute _para1:a,0x97f70,0x165839e4
after _para1:b,0x97fa5,0x27d70988

我们可以借助  clang -rewrite-objc 转换.c文件得到.cpp文件,也可以转换.m也可以得到cpp文件(可能会有些报错)

以下是部分转换后的代码

//这里就是block对象的结构
//imp:函数指针对象,FuncPtr指向具体block实现的函数
//_person2:截获的变量
//isa、flags、funcptr、desc后面会说道。

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};
struct __main1_block_impl_0 {
  struct __block_impl impl;
  struct __main1_block_desc_0* Desc;
  char *_para1;
  __main1_block_impl_0(void *fp, struct __main1_block_desc_0 *desc, char *__para1, int flags=0) : _para1(__para1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
//block实现的函数
static void __main1_block_func_0(struct __main1_block_impl_0 *__cself) {
  char *_para1 = __cself->_para1; // bound by copy

        printf("exute _para1:%s,%p,%p\n",_para1,_para1,&_para1);
    }
//block对象的描述信息(大小等等)
static struct __main1_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main1_block_desc_0_DATA = { 0, sizeof(struct __main1_block_impl_0)};
//这是objc测试函数test
void main1()
{
    char *_para1="a";
    printf("init _para1:%s,%p,%p\n",_para1,_para1,&_para1);
    void(*testBlock)(void)=(void (*)())&__main1_block_impl_0((void *)__main1_block_func_0, &__main1_block_desc_0_DATA, _para1);
    _para1="b";
    printf("before _para1:%s,%p,%p\n",_para1,_para1,&_para1);
    ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
    printf("after _para1:%s,%p,%p\n",_para1,_para1,&_para1);
}

简单分析block截获变量:

1).block初始化

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

传入了参数:函数指针、block描述、外部变量_para1,这时候在block内部对_para1进行了引用,即block的结构体成员_para1引用了的是 这个地址 0x97f70

 : _para1(__para1) 

之后又对_para1做了修改,重新指向了 0x97fa5这块内存,但是不会影响block结构体成员_para1,因为成员_para1指向的是0x97f70。

2).执行block

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

其实还是调用了block对象里的函数对象(_block_imp1)的函数指针(FuncPtr) 所指向的函数__main1_block_func_0,并把block自己作为参数传递进去。

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

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

值得注意的的是,我们在执行之前对_para1修改,但是并没有对block内部产生影响(只是把局部变量_para1重新指向另一块内存(0x97fa5)),而在构造block的时候,block对象内部对截获的变量做了引用(引用的是0x97f70),放在(char *_para1)里,执行block的时候,直接调用函数__main1_block_func_0,函数内部,是引用了block结构体的成员_para1,所以会有这样的结果。

block 解析 - 局部变量,布布扣,bubuko.com

时间: 2024-10-29 01:17:34

block 解析 - 局部变量的相关文章

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之后,对变量修改,就无法同步到block内部,但是对于成员变量,结果却不一样,即时不加__block修饰符,block初始化后,对于block内部引用的变量的修改,也能同步到block内部,并且在block内部可以修改成员变量的值. Demo: 声明两个变量:_person2._p1 @interface KDBlockTest() { NSStrin

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 register, auto, and static

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

block 解析 - 静态变量

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

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的内