回顾

在 上一篇 中我们讲了截获变量特性,对于局部变量,变量不加__block修饰符,在block内部是无法修改变量的值。而且

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

成员变量

对于成员变量,结果却不一样,加了__block和不加__block修饰符效果都是一样的,而且不用区分是引用类型和值类型,block初始化后,对于block内部引用的变量的修改,也能同步到block内部,并且在block内部可以修改成员变量的值。

Demo:

声明两个变量:_person2、_person3

@interface KDBlockTest()
{
NSString *_person2;
__block NSString *_person3;
}

添加测试方法,输出变量的值、地址、指针地址

-(void )test3
{
_person2=@"person2";
_person3=@"person3";
//初始值
NSLog(@"init _person2:%@,%p",_person2,_person2);
NSLog(@"init _person3:%@,%p",_person3,_person3);
void (^myBlock)(int) = ^(int num) {
//block内赋值
_person3=@"person33";
NSLog(@"excuteing _person2:%@,%p",_person2,_person2);
NSLog(@"excuteing _person3:%@,%p",_person3,_person3);
};
//修改前赋值
_person2=@"person22";
NSLog(@"excutebefore _person2:%@,%p",_person2,_person2);
NSLog(@"excutebefore _person3:%@,%p",_person3,_person3);
myBlock();
//block执行后
NSLog(@"excuteafter _person2:%@,%p",_person2,_person2);
NSLog(@"excuteafter _person3:%@,%p",_person3,_person3);
}

执行结果如下:

-- ::11.526 Test[:60b] init _person2:person2,0x10790c
-- ::11.529 Test[:60b] init _person3:person3,0x10791c
-- ::11.530 Test[:60b] excutebefore _person2:person22,0x10797c
-- ::11.531 Test[:60b] excutebefore _person3:person3,0x10791c
-- ::11.532 Test[:60b] excuteing _person2:person22,0x10797c
-- ::11.534 Test[:60b] excuteing _person3:person33,0x10794c
-- ::11.535 Test[:60b] excuteafter _person2:person22,0x10797c
-- ::11.536 Test[:60b] excuteafter _person3:person33,0x10794c

从日志可以看出,

  1. block内部修改了成员变量_person3(没有用__block修饰符),并且同步到block外部,修改前和修改后地址是一样的。
  2. block初始化后,执行前,修改成员变量_person2的值,可以同步到block内部(没有用__block修饰符),修改前和修改后地址是一样的。

我们来看一下clang转换后的代码就会知道原因了

struct __KDBlockTest__test3_block_impl_0 {
struct __block_impl impl;
struct __KDBlockTest__test3_block_desc_0* Desc;
KDBlockTest *self;
__KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, KDBlockTest *_self, int flags=) : self(_self) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};

对于局部变量,block结构体里对应一个变量,都会有一个成员。

对于成员变量,block结构体里只会有一个成员变量,即 KDBlockTest *self,不管你是否用__block修饰了,此时对self产生了强引用

void (*myBlock)(int) = (void (*)(int))&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, self, );

在初始化的时候,把self传到block结构体构造函数里,block对象对self产生了引用,此时我们对成员变量进行修改

_person2=@"person22";
_person3=@"person33";

转换后代码

 (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_3beba7_mi_8;

这段代码大致是修改self的objc变量。下面开始执行block,即调用对应的函数指针。

((void (*)(__block_impl *, int))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock, );
static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself, int num) {
KDBlockTest *self = __cself->self; // bound by copy (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person3))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_3beba7_mi_5;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_3beba7_mi_6,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_3beba7_mi_7,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person3)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person3)));
}

函数实现里通过引用block结构体的成员self,再引用到对应的objc变量_person2和_person3。

小结:

  1. 对于一个、多个成员变量,不管是否用__block修饰(用不用都没任何影响),block结构体会生成一个成员 :self,并且会引用成员变量所属的对象实例 self。
  2. 对于成员变量的修改都是通过对象self指针引用来实现的。
  3. block内部对于成员变量的访问也是通过block结构体对象的成员self 指针引用来实现的。

block 解析 - 成员变量的更多相关文章

  1. block 解析 - 形参变量

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

  2. block 解析 - 静态变量

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

  3. tomcat与springmvc 结合 之---第16篇 servlet如何解析成员变量和DispatcherServlet如何解析

    writedby 张艳涛,用了两个星期将深入刨析tomcat看完了,那么接下来该看什么呢?真是不知道,知识这东西上一个月看的jvm,锁.多线程并发 又都忘了.... tomcat学完,我打算看spri ...

  4. AJPFX解析成员变量和局部变量

    成员变量和局部变量 3.1.成员变量和局部变量 A:在类中的位置不同         * 成员变量:在类中方法外         * 局部变量:在方法定义中或者方法声明上 B:在内存中的位置不同   ...

  5. runtime第二部分成员变量和属性

    接上一篇 http://www.cnblogs.com/ddavidXu/p/5912306.html 转载来源http://www.jianshu.com/p/6b905584f536 http:/ ...

  6. block 解析 - 内存

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

  7. 【iOS 开发】Objective - C 面向对象 - 方法 | 成员变量 | 隐藏封装 | KVC | KVO | 初始化 | 多态

    一. Objective-C 方法详解 1. 方法属性 (1) OC 方法传参机制 Object-C 方法传参机制 : OC 中得参数传递都是值传递, 传入参数的是参数的副本; -- 基本类型 (值传 ...

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

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

  9. block 解析 - block变量

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

随机推荐

  1. Linux命令之切换用户

    一.从 user 用户切换到 root 用户 不管是用图形模式登录 Ubuntu,还是命令行模式登录,我们会发现缺省的用户是 user,但是当我们需要执行一些具有 root 权限的操作(如修还系统文件 ...

  2. 利用Console来调试JS程序、Console用法总结

    http://blog.163.com/zhangmihuo_2007/blog/static/27011075201452522824347/ http://blog.163.com/zhangmi ...

  3. 一步一步学习SignalR进行实时通信_2_Persistent Connections

    原文:一步一步学习SignalR进行实时通信_2_Persistent Connections 一步一步学习SignalR进行实时通信\_2_Persistent Connections Signal ...

  4. Android SDK与API版本的对应关系

    看教程.开发Android程序等很多地方,需要设置Android SDK的版本,而其要我们写的却是API版本的数字, 为了方便查看 Android SDK与API版本的对应关系 我在SDK Manag ...

  5. 腾讯出品的抓包工具Rythem

    Mac下一直没有fiddler这样好用的抓包工具,Charles要收费,难免不爽,昨天调研国内项目的时候,看到腾讯开源了一款抓包工具Rythem,试用了一下,基本配置无问题,但是通配符方面不太搞的定. ...

  6. svn在linux下的使用(svn命令行)ubuntu 删除 新增 添加 提交 状态查询 恢复

    合并步骤:(1)先切换到分支:(2)svn merge trunk . (3)svn sw trunk (4)svn merge --reintegrate branch . svn merge ht ...

  7. Android应用开发基础篇(2)-----Notification(状态栏通知)

    一.概述      Notification这个部件的功能是在状态栏里显示消息提醒,比如有未读的短信或者是未接的电话,那么状态栏里都会有显示,更或者是从某个应用(比如QQ,酷我音乐等等)里按Home键 ...

  8. 揭秘Amazon反应速度超快的下拉菜单

    揭秘Amazon反应速度超快的下拉菜单 如果你以前觉得Amazon这家公司不太在用户体验上下功夫,这篇文章可能会改变你的看法. Amazon主页的左上角有一个商品分类浏览的下拉菜单.当鼠标从菜单中的选 ...

  9. myeclipse优化设置

    一.myeclipse字体设置Window->Preferences->General->Appearance->Colors and Fonts在右侧找到”Aa Test F ...

  10. Android应用开发提高篇(1)-----获取本地IP

    链接地址:http://www.cnblogs.com/lknlfy/archive/2012/02/21/2361802.html 一.概述 习惯了Linux下的网络编程,在还没用智能机之前就一直想 ...