回顾

在 上一篇 中我们讲了截获变量特性,对于局部变量,变量不加__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. vim vimrc

    set nu set shiftwidth= set tabstop= set softtabstop= set autoindent set cindent set smartindent file ...

  2. java_httpservice

    http://blog.csdn.net/maosijunzi/article/details/41045181

  3. MacOS copy图标shell脚本

    不会shell  同学做的... 可以看见在当前文件夹下创建了一个icons文件夹 最后还压缩了文件夹 #!/bin/bash # readPlist [plist] [key] function r ...

  4. 半模对话框 QProgressDialog

    http://doc.qt.io/qt-4.8/qprogressdialog.html progressdialog 用到了qfuture http://blog.csdn.net/liang198 ...

  5. Linux中的IO复用接口简介(文件监视?)

    I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux ...

  6. Java基础—ClassLoader的理解(转)

    默认的三个类加载器 Java默认是有三个ClassLoader,按层次关系从上到下依次是: Bootstrap ClassLoader Ext ClassLoader System ClassLoad ...

  7. php socket 超时设置

    1.php创建socket的方法和设置超时的方法,贴出来分享一下 //如果$waitAckSec=0,则返回成功发送的字节�? //如果$waitAckSec大于0,则返回发送后接收到得内容 //任何 ...

  8. VS2012生成绿色版程序的方法

    方法就是在工程属性里设置: 配置属性-〉常规-〉项目默认值-〉MFC的使用-〉在静态库中使用MFC,见下图 之后重新编译即可.

  9. Codeforces 711E ZS and The Birthday Paradox(乘法逆元)

    [题目链接] http://codeforces.com/problemset/problem/711/E [题目大意] 假设一年有2^n天,问k个小朋友中有两个小朋友生日相同的概率. 假设该概率约分 ...

  10. Jquery读取URL参数

    $(document).ready(function(){ function getQueryString(name) { var reg = new RegExp("(^|&)&q ...