- 浏览: 628216 次
文章分类
最新评论
-
涛声依旧是:
怎么提示掉线,怎么才能在线
基于Tomcat7、Java、WebSocket的服务器推送聊天室 -
dcode:
楼主写的不错,正好遇到点问题,看着你的文章解决了,感谢分享。。 ...
在 Visual Studio 2010 中配置SharpPcap
正确使用Block避免Cycle Retain和Crash
Block简介
Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事。需要注意的是由于Objective-C在iOS中不支持GC机制,使用Block必须自己管理内存,而内存管理正是使用Block坑最多的地方,错误的内存管理 要么导致return cycle内存泄漏要么内存被提前释放导致crash。 Block的使用很像函数指针,不过与函数最大的不同是:Block可以访问函数以外、词法作用域以内的外部变量的值。换句话说,Block不仅 实现函数的功能,还能携带函数的执行环境。
可以这样理解,Block其实包含两个部分内容
- Block执行的代码,这是在编译的时候已经生成好的;
-
一个包含
Block执行时需要的所有外部变量值
的数据结构。 Block将使用到的、作用域附近到的变量的值
建立一份快照拷贝到栈上。
Block与函数另一个不同是,Block类似ObjC的对象,可以使用自动释放池管理内存(但Block并不完全等同于ObjC对象,后面将详细说明)。
Block基本语法
1 2 3 4 5 6 7 8 9 10 11 |
|
定义一个实例函数,该函数返回Block:
1 2 3 4 5 6 7 8 9 |
|
是不是感觉很怪?为了看的舒服,我们把Block类型typedef一下
1 2 3 4 5 6 7 8 9 |
|
Block在内存中的位置
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
- NSGlobalBlock:类似函数,位于text段;
- NSStackBlock:位于栈内存,函数返回后Block将无效;
- NSMallocBlock:位于堆内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
为什么blk1类型是NSGlobalBlock,而blk2类型是NSStackBlock?blk1和blk2的区别在于,blk1没有使用Block以外的任何外部变量,Block不需要建立局部变量值的快照,这使blk1与函数没有任何区别,从blk1所在内存地址0x47d0猜测编译器把blk1放到了text代码段。blk2与blk1唯一不同是的使用了局部变量base,在定义(注意是定义,不是运行)blk2时,局部变量base当前值被copy到栈上,作为常量
供Block使用。执行下面代码,结果是203,而不是204。
1 2 3 4 5 6 7 |
|
在Block内变量base是只读的,如果想在Block内改变base的值,在定义base时要用
__block
修饰:__block int base = 100;
。
1 2 3 4 5 6 7 8 9 |
|
输出将是214,211。Block中使用
__block
修饰的变量时,将取变量此刻运行时的值,而不是定义时的快照。这个例子中,执行sum(1,2)
时,base将取base++
之后的值,也就是201,再执行Blockbase+=10; base+a+b
,运行结果是214。执行完Block时,base已经变成211了。
Block的copy、retain、release操作
不同于NSObjec的copy、retain、release操作:
- Block_copy与copy等效,Block_release与release等效;
- 对Block不管是retain、copy、release都不会改变引用计数retainCount,retainCount始终是1;
- NSGlobalBlock:retain、copy、release操作都无效;
-
NSStackBlock:retain、release操作无效,必须注意的是,NSStackBlock在函数返回后,Block内存将被回收。即使retain也没用。容易犯的错误是[
[mutableAarry addObject:stackBlock]
,在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]
。支持copy,copy之后生成新的NSMallocBlock类型对象。 - NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
- 尽量不要对Block使用retain操作。
Block对不同类型的变量的存取
基本类型
- 局部自动变量,在Block中只读。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值。
1 2 3 4 5 6 7 |
|
- static变量、全局变量。如果把上个例子的base改成全局的、或static。Block就可以对他进行读写了。因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量。
1 2 3 4 5 6 7 8 9 |
|
输出结果是0
4 1
,表明Block外部对base的更新会影响Block中的base的取值,同样Block对base的更新也会影响Block外部的base值。
-
Block变量,被
__block
修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。
Block被另一个Block使用时,另一个Block被copy到堆上时,被使用的Block也会被copy。但作为参数的Block是不会发生copy的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
ObjC对象,不同于基本类型,Block会引起对象的引用计数变化。
先看下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
执行结果为1
1 1 2 1
。
__globalObj
和__staticObj
在内存中的位置是确定的,所以Block
copy时不会retain对象。
_instanceObj
在Block
copy时也没有直接retain_instanceObj
对象本身,但会retain
self。所以在Block中可以直接读写_instanceObj
变量。
localObj
在Block
copy时,系统自动retain对象,增加其引用计数。
blockObj
在Block
copy时也不会retain。
非ObjC对象,如GCD队列dispatch_queue_t。Block copy时并不会自动增加他的引用计数,这点要非常小心。
Block中使用的ObjC对象的行为
1 2 3 4 5 6 |
|
对象obj在Block被copy到堆上的时候自动retain了一次。因为Block不知道obj什么时候被释放,为了不在Block使用obj前被释放,Block retain了obj一次,在Block被释放的时候,obj被release一次。
retain cycle
retain cycle问题的根源在于Block和obj可能会互相强引用,互相retain对方,这样就导致了retain cycle,最后这个Block和obj就变成了孤岛,谁也释放不了谁。比如:
1 2 3 4 |
|
1 2 3 4 5 6 |
|
解决这个问题的办法是使用弱引用打断retain cycle:
1 2 3 4 |
|
1 2 3 4 5 6 |
|
request
被持有者释放后。request
的retainCount变成0,request被dealloc,request释放持有的Block,导致Block的retainCount变成0,也被销毁。这样这两个对象内存都被回收。
1 2 3 4 5 6 |
|
与上面情况类似的陷阱:
1 2 3 |
|
这里self和myBlock循环引用,解决办法同上:
1 2 3 4 |
|
1 2 3 4 5 |
|
这里在Block中虽然没直接使用self,但使用了成员变量。在Block中使用成员变量,retain的不是这个变量,而会retain self。解决办法也和上面一样。
1 2 3 4 5 6 |
|
或者
1 2 3 4 |
|
retain cycle不只发生在两个对象之间,也可能发生在多个对象之间,这样问题更复杂,更难发现
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 |
|
解决办法同样是用__block
打破循环引用
1 2 3 4 5 6 7 |
|
注意:MRC中
__block
是不会引起retain;但在ARC中__block
则会引起retain。ARC中应该使用__weak
或__unsafe_unretained
弱引用。__weak
只能在iOS5以后使用。
Block使用对象被提前释放
看下面例子,有这种情况,如果不只是request
持有了Block,另一个对象也持有了Block。
1 2 3 4 5 6 |
|
这时如果request 被持有者释放。
1 2 3 4 5 6 |
|
这时request已被完全释放,但Block仍被objA持有,没有释放,如果这时触发了Block,在Block中将访问已经销毁的request,这将导致程序crash。为了避免这种情况,开发者必须要注意对象和Block的生命周期。
另一个常见错误使用是,开发者担心retain cycle错误的使用__block
。比如
1 2 3 4 |
|
将Block作为参数传给dispatch_async时,系统会将Block拷贝到堆上,如果Block中使用了实例变量,还将retain self,因为dispatch_async并不知道self会在什么时候被释放,为了确保系统调度执行Block中的任务时self没有被意外释放掉,dispatch_async必须自己retain一次self,任务完成后再release self。但这里使用__block
,使dispatch_async没有增加self的引用计数,这使得在系统在调度执行Block之前,self可能已被销毁,但系统并不知道这个情况,导致Block被调度执行时self已经被释放导致crash。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
这里用dispatch_after模拟了一个异步任务,10秒后执行Block。但执行Block的时候MyClass*
obj
已经被释放了,导致crash。解决办法是不要使用__block
。
from:http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/
相关推荐
对于Retain和Assign属性的理解
retain和copy还有assign的区别
在objective-c中,内存的引用计数一直是一个让人比较...似乎ARC的出现只是让我们解放了双手,由于底层实现依然依赖引用计数,所以开启ARC后,只有对引用计数机制更加了解,才能避免Cycle Retain、Crash等问题的出现。
不幸的是ARC没有循环引用检测器,因此很容易出现RetainCycle现象,从而迫使开发者在编码时要采取特殊的预防措施。ARC中的RetainCycle就像日本B级恐怖电影一样。开始使用Cocoa或CocoaTouch做开发时,你甚至不会在意它...
针对iOS 对象的属性的关键字(retain/assign/readonly等)的区别。彻底解决您的疑惑。
SAS-The RETAIN Statement; Retain的用法,英文版。
@property中的retain,copy,assign区别
本文在翻译过程中发现很多地方直译成中文比较晦涩,所以采用了意译的方式,这不可避免的造成有一些地方可能和原文有一定的出入,所以如果你阅读的时候发现有任何的错误都可以给我发邮件:xyl.layne@gmail.com
详细讲解了ios中常用关键字的使用方法的区别,着重讲解了assign,retain,copy之间的区别
block的循环引用导致的内存泄露的示例及解决办法
改进的嵌入逻辑,避免使用大型密集输入 训练期间评估模型的能力 仅需正面贡献即可训练模型的能力,从而提高了性能 额外的脚本可以评估模型并输出几个辅助图形 运行RETAIN-Keras 要求 的Python 3 如果使用GPU:CUDA...
Harbor_retain 介绍 根据每个项目的docker镜像的** keep_num **删除Harbor的镜像。 图像按创建时间排序。 安装 操作系统:CentOS7 Python 3 $ yum install python3 Python点子安装 $ pip3 install python-...
如果面试官问你开发中是否遇到过retain cycle,你如果说没遇到过,估计已经很难跟面试官继续友好的沟通下去了。 但是这个问题怎么回答呢,网络上千篇一律的答案–>使用Block的时候遇到过,使用__weakSelf 代替 self ...
你⽤用哪些办法实现过多线程? GCD和NSOperationQueue的⽐比较,各⾃自优缺点是什么,平时你是怎么使⽤用的? weak,assign,strong的意义和...怎样破除block产⽣生的retain cycle? 为什么不能在getter⾥里使⽤用点语法?
Block是iOS 4.0之后添加的新特性支持。以前使用UIView的动画,进程要控制动画结束后进行相应的...因为Block往往在后台自动对一些它引用了的对象进行retain操作。具体形式这里就不距离了,大家在使用的时候多体会一下。
我们已经看到 alloc, retain, release, and dealloc 在 GNUstep 中的实现,同时也学到了如下知识:所有的 OC 对象有一
今天小编就为大家分享一篇Pytorch 中retain_graph的用法详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
Informatica skill:One way to retain the value from orevious row within Informatica Mapping