block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了

我们了解到了用__block修饰的变量,可以在block内部修改,__block变量其实对应一个结构体

  1. struct __Block_byref__para1_0 {
  2. void *__isa;
  3. __Block_byref__para1_0 *__forwarding;
  4. int __flags;
  5. int __size;
  6. char *_para1;
  7. };

block结构体相应的也有一个成员引用,这样会增加对局部变量的 _para1引用,在Block销毁的时候引用就释放掉了

  1. struct __main1_block_impl_0 {
  2. struct __block_impl impl;
  3. struct __main1_block_desc_0* Desc;
  4. __Block_byref__para1_0 *_para1; // by ref

block内部 成员变量 使用,可以在block内部修改,对应一个、多个成员变量参数,block结构体内部有一个成员引用

  1. struct __KDBlockTest__test3_block_impl_0 {
  2. struct __block_impl impl;
  3. struct __KDBlockTest__test3_block_desc_0* Desc;
  4. KDBlockTest *self;
  5. }

这样会增加对self的引用,在Block销毁的时候引用就释放掉了

循环引用

在使用的时候需要注意循环引用,即某个对象有一个copy或者strong的 block成员属性,这时block内部直接引用了 成员变量(全局变量) 或者self,这样就产生了self持有 block成员,block成员又持有了self,就会导致循环引用。

我们看以下代码(ARC):

  1. typedef void(^ActionTest)(void);
  2. @interface KDBlockTest()
  3. {
  4. __block NSString *_person2;
  5. ActionTest _action;
  6. }
  1. @implementation KDBlockTest
  2. #pragma mark - system
  3. -(instancetype)init
  4. {
  5. self=[super init];
  6. if(self)
  7. {
  8. [self test3];
  9. }
  10. return self;
  11. }
  12. -(void)dealloc
  13. {
  14. NSLog(@"KDBlockTest dealloc");
  15. }
  16. #pragma mark - private
  17. ////循环引用
  18. -(void )test3
  19. {
  20. _person2=@"person2";
  21. _action= ^(void) {
  22. //block内赋值
  23. NSLog(@"excuteing _person2:%@,%p",_person2,_person2);
  24. };
  25. _action();
  26. }

这样我们执行以下代码:

  1. KDBlockTest *_test=[[KDBlockTest alloc]init];

通过调试发现没有走到dealloc,这里不管成员变量 _person2 是否声明 __block都没有什么影响。

成员变量 这一篇已经详细介绍了,对于block 使用 成员变量、self来说,block内部是直接强引用self的。也就是block持有了self,在这里bock又作为self的一个成员被持有,所以就形成了相互引用,导致无法释放。

——weak

对于上面这种情况,我们引入了__weak解决,__weak不会增加对象的引用计数,而且当指向的内存销毁后,__weak指针会自动置为nil。

我们对上面的代码稍作修改

  1. -(void )test3
  2. {
  3. __weak typeof(self) _weakSelf=self;
  4. _person2=@"person2";
  5. NSLog(@"init:%@,%p,%p",_person2,_person2,&self);
  6. _action= ^(void) {
  7. //block内赋值
  8. NSLog(@"excuteing _person2:%@,%p,%p",_weakSelf.person2,_weakSelf.person2,&_weakSelf);
  9. };
  10. _action();
  11. }

输出日志:

2014-07-29 13:38:30.872 Test[2642:60b] init:person2,0x5b980,0x27dae944
2014-07-29 13:38:30.875 Test[2642:60b] excuteing _person2:person2,0x5b980,0x1562ed44
2014-07-29 13:38:30.876 Test[2642:60b] KDBlockTest dealloc

从日志可以看出block内部使用 person2 、_weakSelf 和外面的 person2 、self 地址是一样的,看来也是引用关系,既达到block内部修改变量的效果,又没有对变量产生强引用。我们来看下转换后的代码:

block结构体的定义:

  1. struct __KDBlockTest__test3_block_impl_0 {
  2. struct __block_impl impl;
  3. struct __KDBlockTest__test3_block_desc_0* Desc;
  4. __weak typeof (self) _weakSelf;
  5. __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self) __weakSelf, int flags=0) : _weakSelf(__weakSelf) {
  6. impl.isa = &_NSConcreteStackBlock;
  7. impl.Flags = flags;
  8. impl.FuncPtr = fp;
  9. Desc = desc;
  10. }
  11. };

重点就在这,使用_weak声明的self,block结构体对应 也生成了一个_weak的self成员。我们在看下 我们的test3 方法:

  1. static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) {
  2. __attribute__((objc_gc(weak))) typeof(self) _weakSelf=self;
  3. (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_1;
  4. NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_2,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&self);
  5. (*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakSelf, 570425344);
  6. ((void (*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)));
  7. }

block初始化的时候把  _weakSelf的地址传入,block内部对_weakSelf进行弱引用。在执行block的时候

  1. static void __KDBlockTest__test3_block_func_0(struct __KDBlockTest__test3_block_impl_0 *__cself) {
  2. __weak typeof (self) _weakSelf = __cself->_weakSelf; // bound by copy
  3. NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_d10f18_mi_3,((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)_weakSelf, sel_registerName("person2")),&_weakSelf);
  4. }

通过取得block结构体的 弱引用对象self 成员来访问相对应的方法 person2 (给对象发消息)。

—weak变量

上面例子,我们稍作修改:

  1. (void )test3
  2. {
  3. _person2=@"person2";
  4. __weak typeof(_person2) _weakPerson2=_person2;
  5. NSLog(@"init:%@,%p,%p",_person2,_person2,&_person2);
  6. NSLog(@"init weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);
  7. _action= ^(void) {
  8. //block内赋值
  9. //_weakPerson2=@"person4";//error ,不能修改
  10. NSLog(@"excuteing _person2:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);
  11. };
  12. _person2=@"person22";
  13. NSLog(@"before:%@,%p,%p",_person2,_person2,&_person2);
  14. NSLog(@"before weak:%@,%p,%p",_weakPerson2,_weakPerson2,&_weakPerson2);
  15. _action();
  16. NSLog(@"after:%@,%p,%p",_person2,_person2,&_person2);
  17. }

输出日志:

2014-07-29 15:29:33.472 Test[2719:60b] init:person2,0x5397c,0x16566db8
2014-07-29 15:29:33.475 Test[2719:60b] init weak:person2,0x5397c,0x27db693c
2014-07-29 15:29:33.476 Test[2719:60b] before:person22,0x539bc,0x16566db8
2014-07-29 15:29:33.477 Test[2719:60b] before weak:person2,0x5397c,0x27db693c
2014-07-29 15:29:33.479 Test[2719:60b] excuteing _person2:person2,0x5397c,0x165b5be4
2014-07-29 15:29:33.480 Test[2719:60b] after:person22,0x539bc,0x16566db8
2014-07-29 15:29:33.481 Test[2719:60b] KDBlockTest dealloc

从日志可以看出:

  1. 直接用__weak修饰符修饰_person2变量也可以,也可以避免循环引用,但是不可以在block内部修改外部 参数的值
  2. 在block外部修改变量指针指向,即把指针指向另外一块内存,block内部无法更新到。

我们来看下转换后的代码:其实和不加__block的局部变量差不多,无非多了一个弱引用,不会对引用计数有影响。

  1. struct __KDBlockTest__test3_block_impl_0 {
  2. struct __block_impl impl;
  3. struct __KDBlockTest__test3_block_desc_0* Desc;
  4. __weak typeof (self->_person2) _weakPerson2;
  5. __KDBlockTest__test3_block_impl_0(void *fp, struct __KDBlockTest__test3_block_desc_0 *desc, __weak typeof (self->_person2) __weakPerson2, int flags=0) : _weakPerson2(__weakPerson2) {
  6. impl.isa = &_NSConcreteStackBlock;
  7. impl.Flags = flags;
  8. impl.FuncPtr = fp;
  9. Desc = desc;
  10. }
  11. };
  1. static void _I_KDBlockTest_test3(KDBlockTest * self, SEL _cmd) {
  2. (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_1;
  3. //声明_weak 变量
  4. __attribute__((objc_gc(weak))) typeof(_person2) _weakPerson2=(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2));
  5. NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_2,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)));
  6. NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_3,_weakPerson2,_weakPerson2,&_weakPerson2);
  7. //初始化block
  8. (*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action))= (void (*)())&__KDBlockTest__test3_block_impl_0((void *)__KDBlockTest__test3_block_func_0, &__KDBlockTest__test3_block_desc_0_DATA, _weakPerson2, 570425344);
  9. (*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2))=(NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_5;
  10. NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_6,(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)),&(*(NSString **)((char *)self + OBJC_IVAR_$_KDBlockTest$_person2)));
  11. NSLog((NSString *)&__NSConstantStringImpl__var_folders_5l_2l25j3tn0wl_3zy1hpsq1rhc0000gp_T_KDBlockTest_f32cef_mi_7,_weakPerson2,_weakPerson2,&_weakPerson2);
  12. ((void (*)(__block_impl *))((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)))->FuncPtr)((__block_impl *)(*(ActionTest *)((char *)self + OBJC_IVAR_$_KDBlockTest$_action)));
  13. }

在声明 _weak变量的时候,生成了一个 弱引用的指针 指向 self的person2变量。在block初始化的时候,把弱引用指针指向的内容地址 传递给了block成员

__weak typeof (self->_person2) _weakPerson2;

block结构体内部通过 成员 _weakPerson2 直接弱引用了外部变量 person2的内容地址。这时候如果把person2指针指向另外一块内存地址,那么肯定是同步不到block内部的,这个和 局部变量  大同小异。

总结:

  1. 声明 __weak typeof(self) _weakSelf=self;  这样block内部 生成一个成员 ,会对self弱引用,对于值类型、引用类型都可以修改,并且修改指针指向都可以同步到任何地方。
  2. 声明 __weak typeof(_person2) _weakPerson2=_person2;  针对某个具体的成员变量使用weak修饰符,这样可以避免循环引用,并且不能再block内部修改_weakPerson2。规则如下:
    • 对值类型的修改,如果block初始化后,对值类型修改,无法同步到block内部。
    • 对于引用类型的修改,如果block初始化后,修改指针指向,即指向另外一块内存,这样也是无法同步到block内部

      对于引用类型的修改,如果block初始化后,对指针指向的内存进行修改,即NSMutableArray add 、remove操作,这样是可以用同步到block内部。

block 解析 - 内存的更多相关文章

  1. block之---内存管理

    首先简单说下MRC和ARC MRC: 手动管理内存,需要自己去释放内存, 如果对象的引用计数器为0时对象就会被释放. 属性修饰策略:assign, retain, copy ARC: ARC是编译器特 ...

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

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

  3. block 的内存结构衍生出来的面试题

    今天在群里看到大佬们在讨论一个面试题,问如下代码在 32bit 和 64bit 系统上分别报什么错误: #import <Foundation/Foundation.h> int main ...

  4. Block解析(iOS)

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

  5. block 解析 - 成员变量

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

  6. block 解析 - 局部变量

    局部变量 block内使用局部变量,一般都是截获变量(只读),截获离block初始化最近的一次的值. 引用官方文档: Stack (non-static) variables local to the ...

  7. block 解析 - 形参变量

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

  8. block 解析 - block变量

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

  9. block 解析 - 简介

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

随机推荐

  1. Latex常用包笔记

    1.hyperref 标签包 \usepackage[colorlinks,linkcolor=black,anchorcolor=blue,citecolor=green]{hyperref} 2. ...

  2. python Memo

    list&tuple 运算 乘以constant >>> x = ((1,2),) >>> x*2 ((1, 2), (1, 2)) >>> ...

  3. c语言中的制表符\t与空格

    (本文不讨论制表符与空格缩进问题) 编程过程中,我们常常用多个空格或制表符分隔两个字符串,那么这两个在显示效果上有什么区别呢? 比较如下两行代码的输出效果 代码1: printf("1\t1 ...

  4. MySql: Column 'XXXX' in field list is ambiguous 错误

    [Err] 1052 - Column 'XXXX' in field list is ambiguous 例如: SELECT id, a.name, price, `describe`, scho ...

  5. 北广传媒RTMP流媒体服务器漏洞

    北广传媒移动电视(http://www.bj-mobiletv.com/)的RTMP流媒体服务器有漏洞 该漏洞可以上我们通过他们的服务器向互联网直播视频 使用任意可以发布RTMP流媒体的客户端(例如: ...

  6. Java HashSet和LinkedHashSet的用法

    Java HashSet和LinkedHashSet的用法 类HashSet和LinkedHashSet都是接口Set的实现,两者都不能保存重复的数据.主要区别是HashSet不保证集合中元素的顺序, ...

  7. poj2509---抽k根烟就换一支,求能抽烟的总数

    #include <stdio.h> #include <stdlib.h> int main() { int now,k; while(scanf("%d %d&q ...

  8. Java 处理json经常使用代码

    本project代码已上传至资源,如有须要,请自行下载. package com.michael; import static org.junit.Assert.assertEquals; impor ...

  9. xcode UIImage图片拉伸

    图片拉伸 +(UIImage*)wlisWithImage:(NSString *)name{ //获取图片 UIImage * img=[UIImage imageNamed:name]; //获取 ...

  10. java中Log4J的使用笔记

    使用Log4J,程序员可以控制日志信息输送的目的地,包括控制台,文件,GUI组件和NT事件记录器,也可以控制每一条日志的输出格式,或通过定义每一条日志信息的级别,更加细致地控制日志的生成过程. Log ...