ARC下循环引用的问题
最初
最近在开发应用时碰到使用ASIHttpRequest后在某些机器上发不出请求的问题,项目开启了ARC,代码是这样写的:
@implement MainController
- (void) fetchUrl{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
[request setCompletionBlock:^{
NSLog(@"completed");
}];
[request startAsynchronous];
}
@end
后来发现原因是request这个变量在退出这个函数后就被释放了,自然发不出请求。因为用了ARC,没法手动调用[request retain]让这个变量不被释放,所以只能把这个变量变成实例变量,让Controller实例存在的过程中一直持有这个变量不释放。
@interface MainController {
ASIHTTPRequest *request;
}
@end @implement MainController
- (void) fetchUrl{
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
[request setCompletionBlock:^{
[self complete];
}];
[request setFailedBlock:^{
NSLog(@"failed");
}];
[request startAsynchronous];
}
@end
问题一
这下发送请求没问题了,但出了另一个问题,XCode编译后提示[self complete]这一行可能会导致循环引用。因为MainController实例持有request, request持有completionBlock,completionBlock又持有MainController,导致循环引用,MainController实例在外界引用计数为0时仍无法被释放,因为自身的变量request里持有MainController实例的引用,其引用计数永远大于1。
导致这样循环引用的原因是在completionBlock里调用的self是一个strong类的引用,会使self引用计数+1,可以保证在调用过程self不会被释放,但在这里不需要这样的保证,可以声明另一个__weak变量指向self,这样在block使用这个变量就不会导致self引用计数+1,不会导致循环引用。
@implement MainController
- (void) fetchUrl{
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
__weak id this = self; [request setCompletionBlock:^{
[this complete];
}];
[request startAsynchronous];
}
@end
这样循环引用问题就解决了,不过__weak只支持iOS5.0以上,5以下的要用__unsafe_unretain代替__weak,区别是对象被释放后__weak声明的变量会指向nil,安全点,__unsafe_unretain不会,变成野指针容易导致应用crash。
问题二
如果在block只是调用下MainController的方法,上面的解决方法就够了,但我的需求是在block里要调用到很多实例变量,包括赋值:
@interface MainController {
ASIHTTPRequest *request;
BOOL isLoading;
UIView *loadingView;
}
@end @implement MainController
- (void) fetchUrl{
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]]; [request setCompletionBlock:^{
isLoading = NO;
loadingView.hidden = NO;
}];
[request startAsynchronous];
}
@end
XCode提示说isLoading = NO和loadingView.hidden = NO两行都可能导致循环引用,这下难办了,对于loadingView,是可以跟self一样再声明一个__weak引用给block用,但像isLoading这样需要赋值的没法这样做,而且使用的实例变量多的情况下每个都另外声明__weak变量也是很烦。想半天想到三个办法:
1
实例变量全部加上get set方法,通过声明的__weak变量访问,缺点是破坏了封装性,把原本私有的实例变量变成公有。
@interface MainController {
ASIHTTPRequest *request;
}
@property (nonatomic, strong) UIView *loadingView;
@property (nonatomic, assign) BOOL isLoading;
@end @implement MainController
@synthesize loadingView, isLoading; - (void) fetchUrl{
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
__weak id this = self; [request setCompletionBlock:^{
this.isLoading = NO;
this.loadingView.hidden = NO;
}];
[request startAsynchronous];
}
@end
2
在类里声明一个方法专门处理,缺点是麻烦,每一个回调都要另外声明一个实例方法,代码变丑。
@interface MainController {
ASIHTTPRequest *request;
BOOL isLoading;
UIView *loadingView;
}
@end @implement MainController
- (void) complete:(ASIHttpRequest *)request
{
isLoading = NO;
loadingView.hidden = NO;
}
- (void) fetchUrl{
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
__weak id this = self;
__weak ASIHttpRequest *_request = request; [request setCompletionBlock:^{
[this complete:request];
}];
[request startAsynchronous];
}
@end
3
在block结束手动释放request。在循环引用里出现的问题是MainController外部引用计数为0时它仍不能释放,但如果我们通过手动设置request=nil,导致request变量指向的对象引用计数为0被释放,它对MainController的引用也就释放了,MainController在外部引用计数为0时就可以正常释放了,解决了循环引用的问题。这个做法的缺点是XCode的警告提示还存在着。
@interface MainController {
ASIHTTPRequest *request;
BOOL isLoading;
UIView *loadingView;
}
@end @implement MainController
- (void) fetchUrl{
request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];
[request setCompletionBlock:^{
isLoading = NO;
loadingView.hidden = NO;
request = nil;
}];
[request startAsynchronous];
}
@end
不知还有没有更好的方法?
ARC下循环引用的问题的更多相关文章
- iOS ARC下循环引用的问题 -举例说明strong和weak的区别
strong:适用于OC对象,作用和非ARC中的retain作用相同,它修饰的成员变量为强指针类型weak:适用于OC对象,作用和非ARC中的assign作用相同,修饰的成员变量为弱指针类型assig ...
- 0c-41-ARC下循环引用问题
1.ARC下循环引入问题 一个人拥有一只狗,一只狗拥有一个主人. 当增加d.owner = p;时形成循环引用. 解决方法:一端用strong,一端用weak. 2.ARC下@property参数 A ...
- OC ARC之循环引用问题(代码分析)
// // main.m // 03-arc-循环引用 // // Created by apple on 13-8-11. // Copyright (c) 2013年 itcast. All ri ...
- 解决ARC的循环引用问题
看看下面的程序有什么问题: BNRItem.h @interface BNRItem : NSObject @property (nonatomic, strong) BNRItem *contain ...
- 八.OC基础加强--1.autorelease的用法 2.ARC下内存管理 3.分类(category)4.block的学习
1.autorelease的用法 1.自动释放池及autorelease介绍 (1)在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的. (2)当一个对象调用auto ...
- ARC下的内存管理
1.ARC下单对象内存管理 局部变量释放对象随之被释放 int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = ...
- 浅谈 关于ARC循环引用得问题
这段时间在研究关于ARC得循环引用导致变量不能释放,在此先介绍一本书英文书: <Pro Multithreading and Memory Management for iOS and OS X ...
- 理解 ARC 下的循环引用
本文由 伯乐在线 - nathanw 翻译,dopcn 校稿.未经许可,禁止转载!英文出处:digitalleaves.com.欢迎加入翻译组. ARC 下的循环引用类似于日本的 B 级恐怖片.当你刚 ...
- ARC下的block导致的循环引用问题解析
ARC下的block导致的循环引用问题解析 更详细细节请参考 http://blog.sina.com.cn/s/blog_8c87ba3b0101m599.html ARC下,copy到堆上的blo ...
随机推荐
- MIP启发式算法:local branching
*本文主要是记录并分享最近学习到的知识,算不上原创 *参考文献见链接 本文主要是讲述local branching算法,主要以M. Fischetti的论文 “Local braching”和Pier ...
- 常用C/C++预处理指令详解
预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查.预处理命令以符号“#”开头. 常用的预处理指令包括: 宏定义:#define 文件包含:#include 条件编译:#i ...
- 玩App怎么赚钱(二)
紧接上篇文章,谈到App前赚钱的一些门道,其实还有很多了,需要你自己去挖掘App到底有什么价值.有价值的东西就能形成交易,而交易的过程中是用金钱作为流通手段,所以说赚钱没那么高大上,它的本质就是价值的 ...
- ubuntu14.04 不能关机,一直停在关机界面
1.emotion: 最近在使用Ubuntu14.04 LTS时,输入shutdown -h now之后,Ubuntu就一直停在关机界面,始终不能shutdown,不得不手动按下电源button.忍受 ...
- [python学习篇][书籍学习][python standrad library][内建类型]迭代器类型
我们已经知道,可以直接作用于for循环的数据类型有以下几种:一类是集合数据类型,如list.tuple.dict.set.str等:一类是generator,包括生成器和带yield的generato ...
- Ruby 符号【转】
Ruby的符号足以让很多初学者迷惑上一段时间,看过本章节后,或许会解开你心中的疑惑. 在Ruby中,一个符号是就是一个Symbol类的实例,它的语法是在通常的变量名前加一个冒号,如 :my_sy Ru ...
- NuGet之控制台管理程序包
NuGet作为VS的扩展程序,已经做好了UI,我们可以通过Manage NuGet Packages 的对话框.这里我们主要说说如何通过控制台进行包管理.使用命令行的方式,其实也是有其好处,对 ...
- 【图文】 使用ant编译和发布java项目
开发JavaEE项目经常会碰到修改代码后,项目没有重新编译的问题.老大给指明了一个解决办法:用ant编译项目. ant是apache基金会下的一个项目,是基于Java语言的构建工具. ...
- Linux Shell系列教程
学习Linux Shell知识,就来Linux大学网(Linuxdaxue.com)! 本系列适合Linux初学者,属于Linux入门级教程,主要介绍了Shell的分类.语法格式以及脚本的使用和编写格 ...
- [OJ#15]TR #2 画心
[OJ#15]TR #2 画心 试题描述 渠是一名画师.渠有一支神奇的画笔,可以画尽因果. 渠要画一幅画,这幅画由N个线段组成,线段从1开始编号,第i条线段有一个特殊的因果值Ai. 由于画太长了,渠不 ...