索尼收音机专卖上卖药的靠谱吗?

block使用小结、在arc中使用block、如何防止循环引用(zz)
zz from&/?p=255
使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试,发现竟然做错了几道,
才知道自己想当然的理解是错误的,所以抽时间学习了下,并且通过一些测试代码进行测试,产生这篇博客。
Block简介(copy一段)
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基本语法
基本语法在本文就不赘述了,同学们自学。
Block的类型与内存管理
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock,
NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。
1、NSGlobalBlock如下,我们可以通过是否引用外部变量识别,未引用外部变量即为NSGlobalBlock,可以当做函数使用。
&&&&//create
a NSGlobalBlock
(^sum)(float, float) =
^(float a, float b){
&&&&&&&&return
&&&&NSLog(@"block
is %@", sum); //block is
&__NSGlobalBlock__: 0x47d0&
2、NSStackBlock如下:
&&&&NSArray
*testArr = @[@"1", @"2"];
(^TestBlock)(void) = ^{
&&&&&&&&NSLog(@"testArr
:%@", testArr);
&&&&NSLog(@"block
is %@", ^{
&&&&&&&&NSLog(@"test
Arr :%@", testArr);
&&&&//block
is &__NSStackBlock__: 0xbfffdac0&
&&&&//打印可看出block是一个
NSStackBlock, 即在栈上,
当函数返回时block将无效
&&&&NSLog(@"block
is %@", TestBlock);
&&&&//block
is &__NSMallocBlock__: 0x75425a0&
&&&&//上面这句在非arc中打印是
NSStackBlock, 但是在arc中就是NSMallocBlock
&&&&//即在arc中默认会将block从栈复制到堆上,而在非arc中,则需要手动copy.
3、NSMallocBlock只需要对NSStackBlock进行copy操作就可以获取,但是retain操作就不行,会在下面说明
Block的copy、retain、release操作
(还是copy一段)
不同于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],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock
copy到堆上,然后加入数组:[mutableAarry
addObject:[[stackBlock copy]
autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
尽量不要对Block使用retain操作。
Block对外部变量的存取管理
基本数据类型
1、局部变量
局部自动变量,在Block中只读。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值。
base = 100;
(^sum)(int, int) = ^ long
(int a, int b) {
&&&&&&&&return
base + a +
&&&&printf("%ld\n",sum(1,2));
这里输出是103,而不是3,
因为块内base为拷贝的常量 100
2、STATIC修饰符的全局变量
因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量.
&&&&static
int base = 100;
(^sum)(int, int) = ^ long
(int a, int b) {
&&&&&&&&base++;
&&&&&&&&return
base + a +
&&&&printf("%ld\n",sum(1,2));
这里输出是4,而不是103,
因为base被设置为了0
&&&&printf("%d\n",
这里输出1,
因为sum中将base++了
3、__BLOCK修饰的变量
Block变量,被__block修饰的变量称作Block变量。
基本类型的Block变量等效于全局变量、或静态变量。
注:BLOCK被另一个BLOCK使用时,另一个BLOCK被COPY到堆上时,被使用的BLOCK也会被COPY。但作为参数的BLOCK是不会发生COPY的
block对于objc对象的内存管理较为复杂,这里要分static
global local
block变量分析、还要分非arc和arc分析
非ARC中的变量
先看一段代码(非arc)
@interface MyClass : NSObject
&&&&NSObject*
_instanceO
@implementation MyClass
NSObject* __globalObj =
- (id) init {
(self = [super
&&&&&&&&_instanceObj
= [[NSObject alloc]
&&&&return
- (void) test {
&&&&static
NSObject* __staticObj = nil;
&&&&__globalObj
= [[NSObject alloc]
&&&&__staticObj
= [[NSObject alloc]
&&&&NSObject*
localObj = [[NSObject alloc]
&&&&__block
NSObject* blockObj = [[NSObject
alloc] init];
&&&&typedef
void (^MyBlock)(void) ;
&&&&MyBlock
aBlock = ^{
&&&&&&&&NSLog(@"%@",
__globalObj);
&&&&&&&&NSLog(@"%@",
__staticObj);
&&&&&&&&NSLog(@"%@",
_instanceObj);
&&&&&&&&NSLog(@"%@",
localObj);
&&&&&&&&NSLog(@"%@",
blockObj);
&&&&aBlock
= [[aBlock copy]
autorelease];
&&&&aBlock();
&&&&NSLog(@"%d",
[__globalObj retainCount]);
&&&&NSLog(@"%d",
[__staticObj retainCount]);
&&&&NSLog(@"%d",
[_instanceObj retainCount]);
&&&&NSLog(@"%d",
[localObj retainCount]);
&&&&NSLog(@"%d",
[blockObj retainCount]);
int main(int argc, char
*argv[]) {
&&&&@autoreleasepool
&&&&&&&&MyClass*
obj = [[[MyClass alloc]
init] autorelease];
&&&&&&&&[obj
&&&&&&&&return
执行结果为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。
ARC中的变量测试
由于arc中没有retain,retainCount的概念。只有强引用和弱引用的概念。当一个变量没有__strong的指针指向它时,就会被系统释放。因此我们可以通过下面的代码来测试。
代码片段1(globalObject全局变量)
NSString *__globalString =
- (void)testGlobalObj
&&&&__globalString
(^TestBlock)(void) = ^{
&&&&&&&&NSLog(@"string
is :%@", __globalString); //string is
/blog/wp-includes/images/smilies/icon_sad.gif"
alt=":(" class="wp-smiley"& null)
&&&&__globalString
&&&&TestBlock();
- (void)testStaticObj
&&&&static
NSString *__staticString =
&&&&__staticString
&&&&printf("static
address: %p\n",
&__staticString);&&&
//static address: 0x6a8c
(^TestBlock)(void) = ^{
&&&&&&&&printf("static
address: %p\n", &__staticString);
//static address: 0x6a8c
&&&&&&&&NSLog(@"string
is : %@", __staticString); //string is
/blog/wp-includes/images/smilies/icon_sad.gif"
alt=":(" class="wp-smiley"& null)
&&&&__staticString
&&&&TestBlock();
- (void)testLocalObj
&&&&NSString
*__localString = nil;
&&&&__localString
&&&&printf("local
address: %p\n",
&__localString); //local address:
0xbfffd9c0
(^TestBlock)(void) = ^{
&&&&&&&&printf("local
address: %p\n", &__localString);
//local address: 0x71723e4
&&&&&&&&NSLog(@"string
is : %@", __localString); //string is : 1
&&&&__localString
&&&&TestBlock();
- (void)testBlockObj
&&&&__block
NSString *_blockString = @"1";
(^TestBlock)(void) = ^{
&&&&&&&&NSLog(@"string
is : %@", _blockString); // string is
/blog/wp-includes/images/smilies/icon_sad.gif"
alt=":(" class="wp-smiley"& null)
&&&&_blockString
&&&&TestBlock();
- (void)testWeakObj
&&&&NSString
*__localString = @"1";
&&&&__weak
NSString *weakString = __localS
&&&&printf("weak
address: %p\n",
&weakString);& //weak
address: 0xbfffd9c4
&&&&printf("weak
str address: %p\n", weakString); //weak str
address: 0x684c
(^TestBlock)(void) = ^{
&&&&&&&&printf("weak
address: %p\n", &weakString); //weak
address: 0x7144324
&&&&&&&&printf("weak
str address: %p\n", weakString); //weak str
address: 0x684c
&&&&&&&&NSLog(@"string
is : %@", weakString); //string is :1
&&&&__localString
&&&&TestBlock();
由以上几个测试我们可以得出:
1、只有在使用local变量时,block会复制指针,且强引用指针指向的对象一次。其它如全局变量、static变量、block变量等,block不会拷贝指针,只会强引用指针指向的对象一次。
2、即时标记了为__weak或__unsafe_unretained的local变量。block仍会强引用指针对象一次。(这个不太明白,因为这种写法可在后面避免循环引用的问题)
循环引用retain
循环引用指两个对象相互强引用了对方,即retain了对方,从而导致谁也释放不了谁的内存泄露问题。如声明一个delegate时一般用assign而不能用retain或strong,因为你一旦那么做了,很大可能引起循环引用。在以往的项目中,我几次用动态内存检查发现了循环引用导致的内存泄露。
这里讲的是block的循环引用问题,因为block在拷贝到堆上的时候,会retain其引用的外部变量,那么如果block中如果引用了他的宿主对象,那很有可能引起循环引用,如:
self.myblock = ^{
&&&&&&&&&&&&[self
doSomething];
&&&&&&&&};
为测试循环引用,写了些测试代码用于避免循环引用的方法,如下,(只有arc的,懒得做非arc测试了)
- (void)dealloc
&&&&NSLog(@"no
cycle retain");
- (id)init
= [super init];
#if TestCycleRetainCase1
&&&&&&&&//会循环引用
&&&&&&&&self.myblock
&&&&&&&&&&&&[self
doSomething];
&&&&&&&&};
#elif TestCycleRetainCase2
&&&&&&&&//会循环引用
&&&&&&&&__block
TestCycleRetain *weakSelf = self;
&&&&&&&&self.myblock
&&&&&&&&&&&&[weakSelf
doSomething];
&&&&&&&&};
#elif TestCycleRetainCase3
&&&&&&&&//不会循环引用
&&&&&&&&__weak
TestCycleRetain *weakSelf = self;
&&&&&&&&self.myblock
&&&&&&&&&&&&[weakSelf
doSomething];
&&&&&&&&};
#elif TestCycleRetainCase4
&&&&&&&&//不会循环引用
&&&&&&&&__unsafe_unretained
TestCycleRetain *weakSelf = self;
&&&&&&&&self.myblock
&&&&&&&&&&&&[weakSelf
doSomething];
&&&&&&&&};
&&&&&&&&NSLog(@"myblock
is %@", self.myblock);
&&&&return
- (void)doSomething
&&&&NSLog(@"do
Something");
int main(int argc, char
*argv[]) {
&&&&@autoreleasepool
&&&&&&&&TestCycleRetain*
obj = [[TestCycleRetain alloc]
&&&&&&&&obj
&&&&&&&&return
经过上面的测试发现,在加了__weak和__unsafe_unretained的变量引入后,TestCycleRetain方法可以正常执行dealloc方法,而不转换和用__block转换的变量都会引起循环引用。
因此防止循环引用的方法如下:
__unsafe_unretained TestCycleRetain *weakSelf =
In manual reference counting
mode,&__block
the effect of not retaining&x.
In ARC mode,&__block
to retaining&x&(just
like all other values). To get the manual reference counting mode
behavior under ARC, you could
use&__unsafe_unretained
As the name&__unsafe_unretained&implies,
however, having a non-retained variable is dangerous (because it
can dangle) and is therefore discouraged. Two better options are to
either use&__weak&(if
you don’t need to support iOS&4 or
OS&X&v10.6), or set
the&__block&value
break the retain cycle.
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。iOS 面试题(四):block 什么时候需要构造循环引用
有没有这样一个需求场景,block 会产生循环引用,但是业务又需要你不能使用 weak self? 如果有,请举一个例子并且解释这种情况下如何解决循环引用问题。
|||||||| 思考时间,想好了请往下翻答案。|||||||||
需要不使用 weak self 的场景是:你需要构造一个循环引用,以便保证引用双方都存在。比如你有一个后台的任务,希望任务执行完后,通知另外一个实例。在我们开源的 YTKNetwork 网络库的源码中,就有这样的场景。
在 YTKNetwork 库中,我们的每一个网络请求 API 会持有回调的 block,回调的 block 会持有 self,而如果 self 也持有网络请求 API 的话,我们就构造了一个循环引用。虽然我们构造出了循环引用,但是因为在网络请求结束时,网络请求 API 会主动释放对 block 的持有,因此,整个循环链条被解开,循环引用就被打破了,所以不会有内存泄漏问题。代码其实很简单,如下所示:
// &YTKBaseRequest.m
- (void)clearCompletionBlock {
& &// nil out to break the retain cycle.
& &self.successCompletionBlock =
& &self.failureCompletionBlock =
总结来说,解决循环引用问题主要有两个办法:
第一个办法是「事前避免」,我们在会产生循环引用的地方使用 weak 弱引用,以避免产生循环引用。
第二个办法是「事后补救」,我们明确知道会存在循环引用,但是我们在合理的位置主动断开环中的一个引用,使得对象得以回收。
下期的问题是:weak 变量在引用计数为 0 时,会被自动设置成 nil,这个特性是如何实现的?
赞助商:掘金是一个高质量的技术社区,让你不错过 iOS 开发的每一条资讯。长按图片二维码识别,技术干货尽在掌握中。
点击“阅读原文”,下载掘金。
先写下这个结论,到时候看自己牛逼还是被打脸。
一些碎碎念,了解我的朋友可以看看。
前言本文只是我的个人心得,不表达任何投资建议,股市有风险,请自己决策。我的错误认识在买美股之前,我总是觉得投
推荐序本文来自泊学的投稿,介绍了 Swift 3.1 的新特性,感谢泊学授权发表。以下为文章正文。正文App
今年的 WWDC 报名还有一天就截止了哟
今天上午,佳木斯爱心永在慈善会的爱心人士寒江,安莉,安红,可心,春红,李强,李四,为你真心,小辛,初夏小甜桶
图文\/陈东(小七手绘) 倍受欢迎的小七POP手绘教程又来啦!六一马上就到,粽子节也近在咫尺,这可都是药店
5月12日下午,天津职业技术师范大学研究生创新基地授牌仪式在天津京津电子商务产业园圆满举行。天津
piu...................认筹礼购物礼抽奖礼礼礼送不停聚惠让利 超越
亲!6月8日-12日苏宁端午节动真格了!端午特惠!端午超省!这么劲爆,我的天啊! 剁手!
我叫唐巧,InfoQ 编辑,《iOS开发进阶》作者,现在在猿题库创业。本账号主要分享我精选的 iOS 开发文章和一些创业感悟。
感谢您的支持,请按照如下步骤取消屏蔽ABBAO的广告():中国领先的IT技术网站
51CTO旗下网站
iOS - NSTimer循环引用的解决办法
NSTimer 我之前没遇到过循环引用的问题,因为我一直都是配对使用,在 viewWillAppear 开启,在 viewWillDisappear 关闭,不关闭的话那么多 timer 挂载在 runloop 上感觉挺影响性能和流畅性的,就像管理内存一样,申请和释放配对使用,就不会泄露了,谁申请谁释放的原则。但是很大的团队的话,别人可能会写错,造成泄露,可以从技术上,团队编程规范上解决他。
作者:佚名来源:| 20:15
在 Controller B 中有一个 NSTimer
@property&(strong,&nonatomic)&NSTimer&*&
你创建了它,并挂载到 main runloop
self.timer&=&[NSTimer&scheduledTimerWithTimeInterval:1&&target:self&selector:@selector(timerAction:)&userInfo:nil&repeats:true];&
然后退出 Controller B 的时候,忘记关掉 timer 了
Controller B 将不会释放,B 与 timer 循环引用。因为创建 timer 的时候把 self 直接写进去了。
既然不能直接传 self,那传 weakSelf 试试
__weak&typeof(self)&weakSelf&=&&&&self.timer&=&[NSTimer&scheduledTimerWithTimeInterval:1&&target:weakSelf&selector:@selector(timerAction:)&userInfo:nil&repeats:true];&
测试结果还是发生了循环引用,B 没有释放,timer 对 weakSelf 这个变量是强引用的,timer -& weakSelf -& B
-& timer,三者之间形成循环引用。
设置一个包装类,包着 Controller B 放进 timer 中,像这样
我认为 Controller B 有几 MB 那么大,泄露了很浪费内存。
WeakWrap 只有几百个字节那么小,泄露了也没关系。
WeakWrap 中对 Controller B 弱引用,WeakWrap 包着 Controller B,传进 timer 中,就算忘记关
timer,也只是泄露了 WeakWrap 和 timer。
理论上还是有内存泄露,只不过比较少,如果一个 Controller 是频繁进出的,进出一次,丢失一个,如果有几十个泄露的 timer 挂在 main
runloop 上会影响性能和流畅性,你想几十个 timer 一起 fire,又调用了 timer 事件响应方法,开销还是挺大的。
NSTimer 已知是会强引用参数 target:self 的了,如果忘记关 timer 的话,传什么进去都会被强引用。干脆实现一个 timer
算了,timer 的功能就是定时调某个方法,NSTimer 的调用时间是不精确的!它挂在 runloop 上受线程切换,上一个事件执行时间的影响。
利用 dispatch_asyn() 定时执行函数。看下面代码。
-&(void)loop&{&&&&&[self&doSomething];&&&&&......&&&&&//&休息&time&秒,再调&loop,实现定时调用&&&&&[NSThread&sleepForTimeInterval:time];&&&&&dispatch_async(self.runQueue,&^{&&&&&&&&&[weakSelf&loop];&&&&&});&&&&&}&
dispatch_async 中调 loop 不会产生递归调用
dispatch_async 是在队列中添加一个任务,由 GCD 去回调 [weakSelf loop]
这办法解决了timer 不能释放,挂在 runloop 不能移除的问题。
利用这方法,我写了个不会发生循环引用的 timer,controller 释放,timer 也自动停止释放,甚至 timer 的 block
里面可以直接写 self,也不会循环引用。github下载地址
NSTimer 我之前没遇到过循环引用的问题,因为我一直都是配对使用,在 viewWillAppear 开启,在 viewWillDisappear
关闭,不关闭的话那么多 timer 挂载在 runloop
上感觉挺影响性能和流畅性的,就像管理内存一样,申请和释放配对使用,就不会泄露了,谁申请谁释放的原则。但是很大的团队的话,别人可能会写错,造成泄露,可以从技术上,团队编程规范上解决他。
比如定一些规范,Controller 退出一定要成功销毁,不能泄露内存。Block 里不能写 self 等等。
【编辑推荐】
【责任编辑: TEL:(010)】
大家都在看猜你喜欢
头条外电头条原创原创
24H热文一周话题本月最赞
讲师:3人学习过
讲师:2人学习过
讲师:3人学习过
精选博文论坛热帖下载排行
《设计模式:可复用面向对象软件的基础》(双语版)是引导读者走入软件设计迷宫的指路明灯,凝聚了软件开发界几十年的设计经验。四位顶尖的...
订阅51CTO邮刊iOS开发中的循环引用-2 - 简书
iOS开发中的循环引用-2
在我的实际项目中会遇到如下几种情况的因为循环引用导致的内存泄漏的情况。
第一种:就是在我们的delegate的机制里面,例如我们在Xcode里面的OS X下面的建立一个Objective-C语言的CommandLineTool工程,我们来实现一下。
首先我们需要两个类,比如一个是Parent和Son,然后Parent拥有一个Son的属性,同时Son需要设置一个代理属性是 Parent来实现buyFood这个代理方法,这样如果Son里面的delegate属性是retain/strong,强引用就会发生循环引用最后谁也释放不掉的现象.
#import &Foundation/Foundation.h&
#import "Son.h"
@interface Parent : NSObject
@property (nonatomic,retain) Son *
#import "Parent.h"
#import "Son.h"
@interface Parent ()&BuyFoodDelegate&
@implementation Parent
- (void)dealloc
NSLog(@"Parent dealloc");
- (void)buyFood {
NSLog(@"excute buy food");
#import &Foundation/Foundation.h&
@protocol BuyFoodDelegate &NSObject&
- (void)buyF
@interface Son : NSObject
@property (nonatomic, weak) id&BuyFoodDelegate&
#import "Son.h"
@implementation Son
- (void)dealloc
NSLog(@"Son dealloc");
#import "Parent.h"
#import "Son.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Son *son = [[Son alloc] init];
Parent *parent = [[Parent alloc] init];
parent.son =
son.delegate =
[parent release];
[son release];
我们再利用Instrument工具来测试一下,看看是否内存泄漏。
instrument.jpg
下面是关于使用Block引起循环引用的原因和处理:
Block简介(copy一段)
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基本语法
基本语法在本文就不赘述了,同学们自学。
Block的类型与内存管理
根据Block在内存中的位置分为三种类型NSGlobalBlock,NSStackBlock, NSMallocBlock。
NSGlobalBlock:类似函数,位于text段;
NSStackBlock:位于栈内存,函数返回后Block将无效;
NSMallocBlock:位于堆内存。
1、NSGlobalBlock如下,我们可以通过是否引用外部变量识别,未引用外部变量即为NSGlobalBlock,可以当做函数使用。
//create a NSGlobalBlock
float (^sum)(float, float) = ^(float a, float b){
return a +
NSLog(@"block is %@", sum); //block is &__NSGlobalBlock__: 0x47d0&
2、NSStackBlock如下:
NSArray *testArr = @[@"1", @"2"];
void (^TestBlock)(void) = ^{
NSLog(@"testArr :%@", testArr);
NSLog(@"block is %@", ^{
NSLog(@"test Arr :%@", testArr);
//block is &__NSStackBlock__: 0xbfffdac0&
//打印可看出block是一个 NSStackBlock, 即在栈上, 当函数返回时block将无效
NSLog(@"block is %@", TestBlock);
//block is &__NSMallocBlock__: 0x75425a0&
//上面这句在非arc中打印是 NSStackBlock, 但是在arc中就是NSMallocBlock
//即在arc中默认会将block从栈复制到堆上,而在非arc中,则需要手动copy.
3、NSMallocBlock只需要对NSStackBlock进行copy操作就可以获取,但是retain操作就不行,会在下面说明
Block的copy、retain、release操作 (还是copy一段)
不同于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],(补:在arc中不用担心此问题,因为arc中会默认将实例化的block拷贝到堆上)在函数出栈后,从mutableAarry中取到的stackBlock已经被回收,变成了野指针。正确的做法是先将stackBlock copy到堆上,然后加入数组:[mutableAarry addObject:[[stackBlock copy] autorelease]]。支持copy,copy之后生成新的NSMallocBlock类型对象。
NSMallocBlock支持retain、release,虽然retainCount始终是1,但内存管理器中仍然会增加、减少计数。copy之后不会生成新的对象,只是增加了一次引用,类似retain;
尽量不要对Block使用retain操作。
Block对外部变量的存取管理
基本数据类型
1、局部变量
局部自动变量,在Block中只读。Block定义时copy变量的值,在Block中作为常量使用,所以即使变量的值在Block外改变,也不影响他在Block中的值。
int base = 100;
long (^sum)(int, int) = ^ long (int a, int b) {
return base + a +
printf("%ld\n",sum(1,2));
// 这里输出是103,而不是3, 因为块内base为拷贝的常量 100
2、STATIC修饰符的全局变量
因为全局变量或静态变量在内存中的地址是固定的,Block在读取该变量值的时候是直接从其所在内存读出,获取到的是最新值,而不是在定义时copy的常量.
static int base = 100;
long (^sum)(int, int) = ^ long (int a, int b) {
return base + a +
printf("%ld\n",sum(1,2));
// 这里输出是4,而不是103, 因为base被设置为了0
printf("%d\n", base);
// 这里输出1, 因为sum中将base++了
3、__BLOCK修饰的变量
Block变量,被__block修饰的变量称作Block变量。 基本类型的Block变量等效于全局变量、或静态变量。
注:BLOCK被另一个BLOCK使用时,另一个BLOCK被COPY到堆上时,被使用的BLOCK也会被COPY。但作为参数的BLOCK是不会发生COPY的
block对于objc对象的内存管理较为复杂,这里要分static global local block变量分析、还要分非arc和arc分析
非ARC中的变量
先看一段代码(非arc)
@interface MyClass : NSObject {
NSObject* _instanceO
@implementation MyClass
NSObject* __globalObj =
- (id) init {
if (self = [super init]) {
_instanceObj = [[NSObject alloc] init];
- (void) test {
static NSObject* __staticObj =
__globalObj = [[NSObject alloc] init];
__staticObj = [[NSObject alloc] init];
NSObject* localObj = [[NSObject alloc] init];
__block NSObject* blockObj = [[NSObject alloc] init];
typedef void (^MyBlock)(void) ;
MyBlock aBlock = ^{
NSLog(@"%@", __globalObj);
NSLog(@"%@", __staticObj);
NSLog(@"%@", _instanceObj);
NSLog(@"%@", localObj);
NSLog(@"%@", blockObj);
aBlock = [[aBlock copy] autorelease];
NSLog(@"%d", [__globalObj retainCount]);
NSLog(@"%d", [__staticObj retainCount]);
NSLog(@"%d", [_instanceObj retainCount]);
NSLog(@"%d", [localObj retainCount]);
NSLog(@"%d", [blockObj retainCount]);
int main(int argc, char *argv[]) {
@autoreleasepool {
MyClass* obj = [[[MyClass alloc] init] autorelease];
[obj test];
执行结果为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。
ARC中的变量测试
由于arc中没有retain,retainCount的概念。只有强引用和弱引用的概念。当一个变量没有__strong的指针指向它时,就会被系统释放。因此我们可以通过下面的代码来测试。
代码片段1(globalObject全局变量)
NSString *__globalString =
- (void)testGlobalObj
__globalString = @"1";
void (^TestBlock)(void) = ^{
NSLog(@"string is :%@", __globalString); //string is block使用小结、在arc中使用block、如何防止循环引用(zz)/blog/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"& null)
__globalString =
TestBlock();
- (void)testStaticObj
static NSString *__staticString =
__staticString = @"1";
printf("static address: %p\n", &__staticString);
//static address: 0x6a8c
void (^TestBlock)(void) = ^{
printf("static address: %p\n", &__staticString); //static address: 0x6a8c
NSLog(@"string is : %@", __staticString); //string is block使用小结、在arc中使用block、如何防止循环引用(zz)/blog/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"& null)
__staticString =
TestBlock();
- (void)testLocalObj
NSString *__localString =
__localString = @"1";
printf("local address: %p\n", &__localString); //local address: 0xbfffd9c0
void (^TestBlock)(void) = ^{
printf("local address: %p\n", &__localString); //local address: 0x71723e4
NSLog(@"string is : %@", __localString); //string is : 1
__localString =
TestBlock();
- (void)testBlockObj
__block NSString *_blockString = @"1";
void (^TestBlock)(void) = ^{
NSLog(@"string is : %@", _blockString); // string is block使用小结、在arc中使用block、如何防止循环引用(zz)/blog/wp-includes/images/smilies/icon_sad.gif" alt=":(" class="wp-smiley"& null)
_blockString =
TestBlock();
- (void)testWeakObj
NSString *__localString = @"1";
__weak NSString *weakString = __localS
printf("weak address: %p\n", &weakString);
//weak address: 0xbfffd9c4
printf("weak str address: %p\n", weakString); //weak str address: 0x684c
void (^TestBlock)(void) = ^{
printf("weak address: %p\n", &weakString); //weak address: 0x7144324
printf("weak str address: %p\n", weakString); //weak str address: 0x684c
NSLog(@"string is : %@", weakString); //string is :1
__localString =
TestBlock();
由以上几个测试我们可以得出:
1、只有在使用local变量时,block会复制指针,且强引用指针指向的对象一次。其它如全局变量、static变量、block变量等,block不会拷贝指针,只会强引用指针指向的对象一次。
2、即时标记了为__weak或__unsafe_unretained的local变量。block仍会强引用指针对象一次。(这个不太明白,因为这种写法可在后面避免循环引用的问题)
循环引用retain cycle
循环引用指两个对象相互强引用了对方,即retain了对方,从而导致谁也释放不了谁的内存泄露问题。如声明一个delegate时一般用assign而不能用retain或strong,因为你一旦那么做了,很大可能引起循环引用。在以往的项目中,我几次用动态内存检查发现了循环引用导致的内存泄露。
这里讲的是block的循环引用问题,因为block在拷贝到堆上的时候,会retain其引用的外部变量,那么如果block中如果引用了他的宿主对象,那很有可能引起循环引用,如:
self.myblock = ^{
[self doSomething];
为测试循环引用,写了些测试代码用于避免循环引用的方法,如下,(只有arc的,懒得做非arc测试了)
- (void)dealloc
NSLog(@"no cycle retain");
- (id)init
self = [super init];
if (self) {
#if TestCycleRetainCase1
//会循环引用
self.myblock = ^{
[self doSomething];
#elif TestCycleRetainCase2
//会循环引用
__block TestCycleRetain *weakSelf =
self.myblock = ^{
[weakSelf doSomething];
#elif TestCycleRetainCase3
//不会循环引用
__weak TestCycleRetain *weakSelf =
self.myblock = ^{
[weakSelf doSomething];
#elif TestCycleRetainCase4
//不会循环引用
__unsafe_unretained TestCycleRetain *weakSelf =
self.myblock = ^{
[weakSelf doSomething];
NSLog(@"myblock is %@", self.myblock);
- (void)doSomething
NSLog(@"do Something");
int main(int argc, char *argv[]) {
@autoreleasepool {
TestCycleRetain* obj = [[TestCycleRetain alloc] init];
经过上面的测试发现,在加了__weak和__unsafe_unretained的变量引入后,TestCycleRetain方法可以正常执行dealloc方法,而不转换和用__block转换的变量都会引起循环引用。
因此防止循环引用的方法如下:
__unsafe_unretained TestCycleRetain *weakSelf =
职业搬砖,也与编程。

我要回帖

更多关于 收音机上卖药的是托么 的文章

 

随机推荐