转载自:http://blog.cnbang.net/tech/2085/

[iOS]ARC下循环引用的问题

2013-8-30

最初

最近在开发应用时碰到使用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

不知还有没有更好的方法?

[iOS]ARC下循环引用的问题相关推荐

  1. ios 内存管理的理解(四)ARC下循环引用问题

    案例 一个人拥有一只狗,一只狗拥有一个主人. @interface Person :NSObject@property(nonatomic,strong) Dog* dog; @end @interf ...

  2. ARC下循环引用的问题

    最初 最近在开发应用时碰到使用ASIHttpRequest后在某些机器上发不出请求的问题,项目开启了ARC,代码是这样写的: @implement MainController - (void) fe ...

  3. [iOS]-NSTimer与循环引用的理解

    目录: 参考的博客: 问题引入 循环引用 简单的循环引用 Block中的循环引用强弱共舞 Delegate中的循环引用 NSTimer 创建NSTimer 销毁NSTimer invalidate f ...

  4. iOS容易造成循环引用的三种场景,就在你我身边!

    ARC已经出来很久了,自动释放内存的确很方便,但是并非绝对安全绝对不会产生内存泄露.导致iOS对象无法按预期释放的一个无形杀手是--循环引用.循环引用可以简单理解为A引用了B,而B又引用了A,双方都同 ...

  5. iOS: NSTimer的循环引用(解决)

    首先有两个概念写在最前: 内存泄漏:系统分配的内存空间在使用完毕之后没有进行及时的回收,称之为发生了内存泄漏. 内存溢出:指在申请内存的时候,没有足够的内存空间可以使用,包括栈溢出和堆溢出. 下面开始 ...

  6. Block的循环引用

    2019独角兽企业重金招聘Python工程师标准>>> 在ios常见的循环引用中曾经提到过block: 看看上面最基本的block循环应用,self包含block,block包含了s ...

  7. C++ — 智能指针的简单实现以及循环引用问题

    http://blog.csdn.net/dawn_sf/article/details/70168930 智能指针 _________________________________________ ...

  8. Mybatis的循环引用

    Mybatis的循环引用 循环引用 数据准备 源码解析 总结 Mybatis中对循环引用及关联查询都做了很好的处理.博主觉得这块非常难.这里只是把我知道的讲出来. 循环引用 什么循环引用博主这里就不介 ...

  9. iOS开发笔记(二):block循环引用

    写这篇文章的缘由是第一次面试时被问到了block循环引用的问题,当时回答的不是很好,首先要明确的是,block是否用copy修饰决定不了循环引用的产生,在此再一次进行补强,有不对的地方还请多多指教. ...

最新文章

  1. 三星会使用华为的鸿蒙系统,【图片】华为鸿蒙系统的厉害之处在于 你可能非用不可 !【手机吧】_百度贴吧...
  2. JBoss AS 8中的Java EE 7和EJB 3.2支持
  3. 英语口语 week13 Monday
  4. 【PHP 扩展开发】Zephir 基础篇
  5. CIO如何跟“IT盲”领导沟通
  6. hadoop安装详细步骤_LED透明屏安装步骤详细说明
  7. HTML - 元素/标签和属性基础
  8. 最近,华为应用市场上线了一个服务
  9. TDD测试驱动开发案例【水货】
  10. java开发pc端和移动端的区别,移动端和PC端的区别
  11. unity3d 官方demo入门概念
  12. 在ArcGIS中ArcCatalog(Arcmap)快速选中多个要素
  13. 基于bing 搜索引擎和 Microsoft Academic Search 的高校申请指南的NABC分析
  14. AI在零售业的五大应用场景:消费者喜欢苹果时,不要给他一箱梨
  15. Ubuntu 设置时间同步
  16. 个人空间岁末大回报活动12月26日获奖名单
  17. Java中关于队列与栈的区别
  18. 伪指令-宏定义:使用#define定义一个宏,宏体中包含命名空间信息导致vs大量报红,但编译和运行却正常
  19. android触摸效果,Android开发进阶:仿MIUI12控件触摸反馈效果(下沉+倾斜)附源码...
  20. 余弦相似度-字符串相似度计算(scala)

热门文章

  1. Getx - 如何使用路由管理页面
  2. macbook黑屏_Macbook苹果笔记本电脑开机黑屏如何解决【解决方法】
  3. 有华为的HCIP证书会更好找工作吗?
  4. [网络工程师]-路由协议-IGRP协议
  5. 记录一个关于oracle数据库us7ascii字符集解决的方法
  6. javaWeb(b站狂神说)
  7. 计算机常用单词 pdf,关于计算机的常用词汇.pdf
  8. X006---交叉表(Cross Tab)和转置(Transpose)
  9. spring boot整合Quartz 在Job类中注入其他对象报空指针异常java.lang.NullPointerException at com.sxt.quartz.QuartzDemo.e
  10. 【日语】编程相关日语词汇