局部变量

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 )test3
{
NSString *_person2=@"person2";
NSMutableArray *_listTest=[[NSMutableArray alloc]init];
//初始值
NSLog(@"init _person2:%@,%p",_person2,_person2);
NSLog(@"init _listTest:%@,%p",_listTest,_listTest);
void (^myBlock)(int) = ^(int num) {
//block内赋值
// _weakPerson2=@"person22";
NSLog(@"excuteing _person2:%@,%p",_person2,_person2);
NSLog(@"excuteing _listTest:%@,%p",_listTest,_listTest);
};
//修改前赋值
_person2=@"person22";
[_listTest addObject:@""];
NSLog(@"excutebefore _person2:%@,%p",_person2,_person2);
NSLog(@"excutebefore _listTest:%@,%p",_listTest,_listTest);
myBlock();
//block执行后
NSLog(@"excuteafter _person2:%@,%p",_person2,_person2);
NSLog(@"excuteafter _listTest:%@,%p",_listTest,_listTest);
}

输出一下结果:

-- ::29.460 Test[:60b] init _person2:person2,0xb18ec
-- ::29.463 Test[:60b] init _listTest:(
),0x17d98560
-- ::29.464 Test[:60b] excutebefore _person2:person22,0xb193c
-- ::29.465 Test[:60b] excutebefore _listTest:( ),0x17d98560
-- ::29.467 Test[:60b] excuteing _person2:person2,0xb18ec
-- ::29.468 Test[:60b] excuteing _listTest:( ),0x17d98560
-- ::29.470 Test[:60b] excuteafter _person2:person22,0xb193c
-- ::29.471 Test[:60b] excuteafter _listTest:( ),0x17d98560
 从日志可以看出:block内部对于可变、不可变的变量都无法修改,而且
  1. 在block初始化后对于NSString 变量  _person2 的修改,并没有同步到block内部,因为这时block外部的变量_person2指针重新指向另外一块内存
  2. 在block初始化后对于NSMutableArray变量 _listTest 的修改,同步到block内部,因为这时block外部的变量 _listTest 指针指向的内存地址没有变,只是对这块内存的值进行了操作。
我们可以借助  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 __KDBlockTest__test3_block_impl_0 {
struct __block_impl impl;
struct __KDBlockTest__test3_block_desc_0* Desc;
NSString *_person2;
NSMutableArray *_listTest;
__KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, NSString *__person2, NSMutableArray *__listTest, int flags=) : _person2(__person2), _listTest(__listTest) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//block实现的函数
static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself, int num) {
NSString *_person2 = __cself->_person2; // bound by copy
NSMutableArray *_listTest = __cself->_listTest; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_4,_person2,_person2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_5,_listTest,_listTest);
}
//block对象的描述信息(大小等等)
static struct __main1_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main1_block_desc_0_DATA = { , sizeof(struct __main1_block_impl_0)};
//这是objc测试函数test
static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) {
NSString *_person2=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_1;
NSMutableArray *_listTest=((id (*)(id, SEL))(void *)objc_msgSend)((id)((id (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init")); NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_2,_person2,_person2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_3,_listTest,_listTest);
void (*myBlock)(int) = (void (*)(int))&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _person2, _listTest, ); _person2=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_6;
((void (*)(id, SEL, id))(void *)objc_msgSend)((id)_listTest, sel_registerName("addObject:"), (id)(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_7);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_8,_person2,_person2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_9,_listTest,_listTest);
((void (*)(__block_impl *, int))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock, ); NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_10,_person2,_person2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_11,_listTest,_listTest);
}

简单分析block截获变量:

 1).block初始化

void (*myBlock)(int) = (void (*)(int))&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _person2, _listTest, );
传入了参数:函数指针、block描述、外部变量 _person2 和 _listTest,这时候在block内部对 _person2、_listTest 进行了引用

: _person2(__person2), _listTest(__listTest)
  1. 在block初始化后,我们对 _person2 做了修改,重新指向了 0xb193c 这块内存,但是不会影响block结构体成员_person2,因为成员 _person2 指向的是 0xb18ec。
  2. 向 _listTest 数组中添加了一个元素,并没有改变它的内存地址,依然还是 0x17d98560 
_person2=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_6;
((void (*)(id, SEL, id))(void *)objc_msgSend)((id)_listTest, sel_registerName("addObject:"), (id)(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_7);
2).执行block
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);

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

static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself, int num) {
NSString *_person2 = __cself->_person2; // bound by copy
NSMutableArray *_listTest = __cself->_listTest; // bound by copy NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_4,_person2,_person2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_6946c5_mi_5,_listTest,_listTest);
}

总结:对于局部变量,变量不加__block修饰符,在block内部是无法修改变量的值。而且

  1. 对值类型的修改,如果block初始化后,无法同步到block内部
  2. 对于引用类型的修改,如果block初始化后,修改指针指向,即指向另外一块内存,这样也是无法同步到block内部
  3. 对于引用类型的修改,如果block初始化后,对指针指向的内存进行修改,即NSMutableArray add 、remove操作,这样是可以用同步到block内部,但block内部同样无法修改。

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

  1. Block解析(iOS)

    1. 操作系统中的栈和堆 我们先来看看一个由C/C++/OBJC编译的程序占用内存分布的结构: 栈区(stack):由系统自动分配,一般存放函数参数值.局部变量的值等.由编译器自动创建与释放.其操作方 ...

  2. block 解析 - 内存

    block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了 我们了解到了用__block修饰的变量,可以在block内部修改,__block ...

  3. block 解析 - 成员变量

    回顾 在 上一篇 中我们讲了截获变量特性,对于局部变量,变量不加__block修饰符,在block内部是无法修改变量的值.而且 对值类型的修改,如果block初始化后,无法同步到block内部 对于指 ...

  4. block 解析 - 形参变量

    block形参 之前漏了一篇block形参的介绍,这里给补上. block形参就是定义block带的参数,和函数的参数使用一样,我们可以在block随意使用修改block形参. 我们来看个例子: 我们 ...

  5. block 解析 - block变量

    block变量 上一篇 讲的是block静态变量的特性,这里我们来看一下_block变量.引用官方: You can specify that an imported variable be muta ...

  6. block 解析 - 简介

    简介 block 类似标准的c函数,除了一些函数体一些可执行的代码,还可以把变量绑定到自动栈或者托管堆上.....和js里的闭包.c# lambda表达式有些类似,实质是一个函数指针.与函数指针的区别 ...

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

    1. 前言 Block:带有自动变量(局部变量)的匿名函数.它是C语言的扩充功能.之所以是拓展,是因为C语言不允许存在这样匿名函数. 1.1 匿名函数 匿名函数是指不带函数名称函数.C语言中,函数是怎 ...

  8. iOS开发之block解析

    1. block的本质是一个Objective-C的对象,为什么这么说? 在Objective-C中,runtime会在执行时依据对象的isa指针的指向,来度额定这个对象的类型,也能够觉得一个对象,它 ...

  9. block 解析 - 静态变量

    静态变量 上一篇 我们了解了block全局变量的使用,静态变量和全局变量一样,可以直接在block内部使用,也可以在block内部修改 引用官方文档: Global variables are acc ...

随机推荐

  1. USACO Section 3.3 Camlot(BFS)

    BFS.先算出棋盘上每个点到各个点knight需要的步数:然后枚举所有点,其中再枚举king是自己到的还是knight带它去的(假如是knight带它的,枚举king周围的2格(网上都这么说,似乎是个 ...

  2. mysql在查询结果中增加排序字段

    ELECT userId , () AS runRank , mostFast1 FROM user_info, ()) b WHERE mostFast1 IS NOT NULL ORDER BY ...

  3. 公选网站作业4_2.php

    序:   此实验的功能是只有已经登陆或者已经注册的用户才有发表留言的资格,否则只能够浏览留言. 1.  主要文件: 4_2login.html// 登陆的主页面 4_2login.php  //处理登 ...

  4. poj 3740 Easy Finding 精确匹配

    题目链接 dlx的第一题, 真是坎坷..... #include <iostream> #include <vector> #include <cstdio> #i ...

  5. poj 1206

    /** 题意: 给定一序列,置换k次之后 输出其状态 置换: 考察循环节长度, 思路: 分别求出每个元素的循环节的大小,用k 模其大小,大的k次之后的位置, 输出即可 **/ #include < ...

  6. 使用COCOS2D-X JSB开发,在js页面中设置iOS键盘模式

    XYSDK.h void setKeyboardType(int type); XYSDK.cpp voidXYSDK::setKeyboardType(int type) { #if (CC_TAR ...

  7. 【HDU】病毒侵袭持续中(AC自己主动机+map)

    一開始一直WA,之后发现这道题不止一组输入,改成多组输入之后就过了. 利用map把每一个字符串映射到它相应的结点上即可了. 11909467 2014-10-19 11:54:00 Accepted ...

  8. Foundation 框架 归档

    一.使用XML属性列表归档 此方法适用于NSString.NSDictionary.NSarray.NSDate.NSnumber,其中atomically参数表示先将字典写入临时备份文件,成功之后, ...

  9. 从零开始写驱动——vfd专用驱动芯片HT16514并行驱动程序编写

    前言 一直看别人搞的 vfd 很漂亮,前段时间淘了个 vfd 模块来,但没有模块资料,还好芯片没有打磨的,良心商家啊.周末抽空来研究一下这个东西. 从零开始 打开外壳 测试线路 查看芯片是 HT165 ...

  10. SPOJ 7001 VLATTICE - Visible Lattice Points(莫比乌斯反演)

    题目链接:http://www.spoj.com/problems/VLATTICE/ 题意:求gcd(a, b, c) = 1    a,b,c <=N 的对数. 思路:我们令函数g(x)为g ...