ARC下循环引用的问题

最初

最近在开发应用时碰到使用ASIHttpRequest后在某些机器上发不出请求的问题,项目开启了ARC,代码是这样写的:


1

2

3

4

5

6

7

8

9

@implement MainController

- (void) fetchUrl{

    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:currUrl]];

    [request setCompletionBlock:^{

        NSLog(@"completed");

    }];

    [request startAsynchronous];

}

@end

后来发现原因是request这个变量在退出这个函数后就被释放了,自然发不出请求。因为用了ARC,没法手动调用[request retain]让这个变量不被释放,所以只能把这个变量变成实例变量,让Controller实例存在的过程中一直持有这个变量不释放。


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

@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,不会导致循环引用。


01

02

03

04

05

06

07

08

09

10

11

@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里要调用到很多实例变量,包括赋值:


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

@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变量访问,缺点是破坏了封装性,把原本私有的实例变量变成公有。


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

@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

在类里声明一个方法专门处理,缺点是麻烦,每一个回调都要另外声明一个实例方法,代码变丑。


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

@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的警告提示还存在着。


01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

@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

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

时间: 2024-07-29 22:52:47

ARC下循环引用的问题的相关文章

ARC下的引用计数----debug

OBJC_EXTERN int _objc_rootRetainCount(id); _objc_rootRetainCount(self.QRcodeVc); NSLog(@"_objc_rootRetainCount(qr)%d",_objc_rootRetainCount(self.QRcodeVc)); 由于ARC是系统管理内存,虽然省却了大部分的内存管理,但是一旦循环引用集很难排除,这里是ARC下debug模式打印对象的引用计数,只能是debug!

解决ARC的循环引用问题

看看下面的程序有什么问题: BNRItem.h @interface BNRItem : NSObject @property (nonatomic, strong) BNRItem *containedItem; @property (nonatomic, strong) BNRItem *container; ... @end BNRItem.m #import "BNRItem.h" @implementation BNRItem ... - (void)setContained

ARC下的内存管理

1.ARC下单对象内存管理 局部变量释放对象随之被释放 int main(int argc, const char * argv[]) { @autoreleasepool { Person *p = [[Person alloc] init]; } // 执行到这一行局部变量p释放 // 由于没有强指针指向对象, 所以对象也释放 return 0; } 清空指针对象随之被释放 int main(int argc, const char * argv[]) { @autoreleasepool

八.OC基础加强--1.autorelease的用法 2.ARC下内存管理 3.分类(category)4.block的学习

1.autorelease的用法   1.自动释放池及autorelease介绍 (1)在iOS程序运行过程中,会创建无数个池子,这些池子都是以栈结构(先进后出)存在的. (2)当一个对象调用autorelease时,会将这个对象放到位于栈顶的释放池中 . 2.为什么会有autorelease? OC的内存管理机制中比较重要的一条规律是:谁申请,谁释放. 但有些情况下,开发者并不能确定某些对象何时释放.这时候就需要自动释放池. 它的好处是: (1)不需要再关心对象释放的时间 : (2)不需要再关

浅谈 关于ARC循环引用得问题

这段时间在研究关于ARC得循环引用导致变量不能释放,在此先介绍一本书英文书: <Pro Multithreading and Memory Management for iOS and OS X with ARC, Grand Central Dispatch, and Blocks> (<iOS与OS X多线程和内存管理>) 建议读英文原版,中文版看的我稀里糊涂的,后来被迫去看原版.这本书介绍了关于ARC的自动引用机制,这里的机制类似:当C语言的局部变量离开他的作用域之后就会被清

理解 ARC 下的循环引用

本文由 伯乐在线 - nathanw 翻译,dopcn 校稿.未经许可,禁止转载!英文出处:digitalleaves.com.欢迎加入翻译组. ARC 下的循环引用类似于日本的 B 级恐怖片.当你刚成为苹果开发者,你或许不会关心他们的存在.直到某天你的一个 app 因内存泄露而闪退,你才突然意识到他们的存在,并且发现循环引用像幽灵一样存在于代码的各个角落.年复一年,你开始学会如何处理循环引用,检测和避免它们,但是这部片子的恐怖结局还是在那里,随时可能出现. ARC 令许多开发者(包括我)感到失

[转]iOS中ARC下Block的循环引用

[ARC的特性] ARC下,所有NSObject类型指针, 1. 默认为__strong类型 2. 可以显示的指定为__weak类型,__weak类型指针在所指向对象销毁后会自动置为nil 3. __autorelesing类型用于inout参数类型 ARC下,当一个函数返回一个NSObject指针时,编译器会帮我们实现autorelease调用.例如: return pObject; 编译器会帮我们扩展为 return [pObject autorelease]; ARC下,不能显式relea

block使用小结、在arc中使用block、如何防止循环引用

引言 使用block已经有一段时间了,感觉自己了解的还行,但是几天前看到CocoaChina上一个关于block的小测试主题 : [小测试]你真的知道blocks在Objective-C中是怎么工作的吗?,发现竟然做错了几道, 才知道自己想当然的理解是错误的,所以抽时间学习了下,并且通过一些测试代码进行测试,产生这篇博客. Block简介(copy一段) Block作为C语言的扩展,并不是高新技术,和其他语言的闭包或lambda表达式是一回事.需要注意的是由于Objective-C在iOS中不支

objective-c启用ARC时的内存管理 (循环引用)

PDF版下载:http://download.csdn.net/detail/cuibo1123/7443125          在Objective-C中,内存的引用计数一直是一个让人比较头疼的问题.尤其是当引用计数涉及到arc.blocks等等的时候.似乎ARC的出现只是让我们解放了双手,由于底层实现依然依赖引用计数,所以开启ARC后,只有对引用计数机制更加了解,才能避免Cycle Retain.Crash等问题的出现. 但是由于使用ARC可以显著提高编码效率,所以建议尽量启用arc,本文