循环引用 -- id 为什么是 assign 而不是 retain

什么是循环引用

简单来说,循环引用就是:A 保留了 B, B 保留了 A, 造成 A 和 B 都不能被释放。

id 为什么是 assign 而不是 retain

从文章标题大概也能猜到, id  是 assign 而不是 retain 的原因是跟你循环引用有关系了。原因也确实如此。

id 之所以是 assign 而不是 retain ,就是因为 retain 可能 会导致循环引用,注意这里的用词是 可能,而不是一定。

下面就用一个实例来说明情况。

注意要严格遵循内存管理的

一、先创建一个协议,协议里有一个方法,方法的功能是改变颜色

1 #import <Foundation/Foundation.h>
2
3 @protocol ZYChangeColorDelegate <NSObject>
4
5 - (void)changeColor;
6
7 @end

二、创建两个视图控制器类

1. 视图控制器 A  和 B ,将两个视图控制器都加进导航,跳转到 B 时,点击 B 上的按钮能够改变 A 的背景色。这里为了说明 id 为什么是 assign 而不是 retain,使用代理传值,其他方法不做讨论。 A 、B 上的按钮都是通过 XIB 实现.

1.1 首先是使用 retain 不会导致循环引用的方式

入口类里部分代码

1 ZYAViewController *aViewController = [[ZYAViewController alloc] init]; // A  +1 =1
2 UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:aViewController];//A +1 +1 =2
3 self.window.rootViewController = navigationController; // A  +1+1 +1 = 3
4 [aViewController release]; // A 3 -1 = 2
5 [navigationController release]; // A 2 -1 = 1

从入口类开始仔细计算 A 和 B 的引用计数

 1 // A
 2 #import <UIKit/UIKit.h>
 3
 4 #import "ZYChangeColorDelegate.h"
 5
 6 @interface ZYAViewController : UIViewController <ZYChangeColorDelegate>
 7
 8 @end
 9
10 // ----------------------------------------------------------------------------------
11
12 #import "ZYAViewController.h"
13 #import "ZYBViewController.h"
14
15 @implementation ZYAViewController
16
17
18 - (void)viewDidLoad
19 {
20     [super viewDidLoad];
21
22 }
23
24 - (IBAction)goToNext:(id)sender
25 {
26     ZYBViewController *bViewController = [[ZYBViewController alloc] init];  // B +1
27     bViewController.delegate = self; // A 1 +1 = 2
28     [self.navigationController pushViewController:bViewController animated:YES]; // B 1 +1 = 2
29     [bViewController release];  // B 2 -1 = 1
30 }
31
32 #pragma mark - 协议方法
33 - (void)changeColor
34 {
35     self.view.backgroundColor = [UIColor redColor];
36 }
37
38 - (void)dealloc
39 {
40     [super dealloc];
41 }
42
43 @end
 1 // B
 2 #import <UIKit/UIKit.h>
 3
 4 #import "ZYChangeColorDelegate.h"
 5
 6 @interface ZYBViewController : UIViewController
 7
 8 @property (nonatomic, retain) id <ZYChangeColorDelegate> delegate;
 9
10 @end
11
12 // ------------------------------------------------------------------------------
13 #import "ZYBViewController.h"
14
15 @implementation ZYBViewController
16
17 - (void)viewDidLoad
18 {
19     [super viewDidLoad];
20
21 }
22
23 - (IBAction)changeABackgroundColor:(id)sender
24 {
25     [_delegate changeColor];
26 }
27
28 - (void)dealloc
29 {
30     [_delegate release];   // A 2 -1 = 1
31     [super dealloc];
32 } // 当点击导航栏上的返回按钮,即将 B 从导航控制器 pop 出来,引用计数的变化为 B 1 -1 = 0, 此时会调用 B 的 dealloc 方法,将 A 的引用计数减 1,当 A 也从导航中 pop 出来(这里 A  是根属兔控制器,之恩个等到程序结束了)时,引用计数为 A 1 -1 = 0,会调用 A 的 dealloc 方法。

上面的这种情况是不会导致循环引用的,因为 A 和 B 的引用计数都在适当的时候变为 0 ,都能够调用各自的 dealloc 方法。

下面是回引起循环引用的情况

2. 使用 retain 导致循环引用,值需要将 A 稍加修改:在 A 中添加一个属性(或者成员变量)

 1 // A
 2 #import <UIKit/UIKit.h>
 3
 4 #import "ZYChangeColorDelegate.h"
 5 #import "ZYBViewController.h"
 6
 7 @interface ZYAViewController : UIViewController <ZYChangeColorDelegate>
 8
 9 @property (nonatomic, retain) ZYBViewController *bViewController;
10
11 @end
12
13 // -----------------------------------------------------------------------------
14 #import "ZYAViewController.h"
15
16 @implementation ZYAViewController
17
18
19 - (void)viewDidLoad
20 {
21     [super viewDidLoad];
22
23 }
24
25 - (IBAction)goToNext:(id)sender
26 {   // 注意与第一种写法的不同
27     _bViewController = [[ZYBViewController alloc] init];  // B +1 = 1
28     _bViewController.delegate = self;  // A 1 +1 = 2
29     [self.navigationController pushViewController:_bViewController animated:YES];  // B 1 +1 = 2
30 }
31
32 #pragma mark - 协议方法
33 - (void)changeColor
34 {
35     self.view.backgroundColor = [UIColor redColor];
36 }
37
38 - (void)dealloc
39 {
40     [_bViewController release];
41     [super dealloc];
42 }
43
44 @end

现在在从入口类开始计算 A、B 的引用计数:此时但从 B 界面离开的时候,B 从导航中 pop 出来,B 的引用计数减 1 ,变为 1,dealloc 方法是在 其引用计数为 0 的时候才回调用,所以此时 A 就不能得到一次 释放,引用计数为 2 ,当 A pop 出导航的时候,其引用计数为 1,也不能调用 dealloc  方法。所以 A 和 B 都不能被释放,这样就形成了这样一种形式:

B 要释放自己 出栈后发现自己引用计数为 1 然后发现只要 A 的 dealloc 方法执行了 B 就可以释放了 找 A 去

A 想要执行自己的 dealloc 方法 要释放自己 发现如果导航释放了自己,自己的引用计数还为 1  然后发现只要 B 的 dealloc 方法执行了 A 就可以释放了 找 B 去

这样就造成了因为循环引用,双方都不能释放自己。

三、使用 assign 避免循环引用

使用 assign,不会使 引用计数 加 1,避免出现循环引用的情况。

时间: 2024-10-16 20:06:45

循环引用 -- id 为什么是 assign 而不是 retain的相关文章

delegate或者protocol申请属性的时候为什么用assign而不是retain

首先delegate要使用assign而不是retain,这个问题大家通过看iOS的api就可以了,最典型的是tabView里面的delegate和datasource都是用的assign. 那为什么要使用assign而不是retain呢? 首先,考虑类的设计模式,类与类只见的大体关系有继承和聚合的关系,当我们使用聚合的时候该对象就拥有聚合的对象,这时候我们就需要retain使引用计数器+1来控制该对象的内存管理,所以我的感觉retain和copy的一项能力就是拥有该对象的内存管理权. 下面就得

iOS 循环引用 委托 (实例说明)

如何避免循环引用造成的内存泄漏呢: 以delegate模式为例(viewcontroller和view之间就是代理模式,viewcontroller有view的使用权,viewcontroller同时也是view的代理(处理view中的事件)): UserWebService.h #import //定义一个ws完成的delegate @protocol WsCompleteDelegate @required -(void) finished;//需要实现的方法 @end @interface

OC中的循环引用

造成循环引用的原因: 比如在main函数中创建了两个类的对象A和B,现在引用计数都是1.现在让A和B互相引用(A有一个属性是B对象),属性说明是retain,B有一个属性是A的对象,属性说明是retain),现在两个对象的引用计数都增加1,都变成2. 现在执行[A release]; [B release]; 此时创建出来得main函数已经释放了自己对对象的持有权,但此时A和B的引用计数都还是1,因为他们互相引用了. 这时你发现A和B将无法释放,因为想要释放A必须先释放B,在B的的dealloc

lua序列化(支持循环引用)

lua序列化 支持key类型为string, number 支持value类型为string, number, table 支持循环引用 支持加密序列化 支持loadstring反序列化 使用示例 local t = { a = 1, b = 2} local g = { c = 3, d = 4, t} t.rt = g local ser_str = ser(g) local unser_table = loadstring(sered)() 原理详解 采用递归序列化表的方式实现,并且支持循

(转载)OC学习篇之---循环引用问题

在之前的一片文章中,我们介绍了数组操作对象的时候引用问题以及自动释放池的概念,今天我们继续来看一下引用计数中一个痛疼的问题:循环引用 关于循环引用的问题,这里就不做太多解释了,就是多个对象之间相互引用,形成环状. 来看一个具体的例子:Dog类和Person类之间相互引用 Dog.h 1 // 2 // Dog.h 3 // 29_CyclePointer 4 // 5 // Created by jiangwei on 14-10-13. 6 // Copyright (c) 2014年 jia

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,本文

block本质探寻八之循环引用

说明:阅读本文,请参照之前的block文章加以理解: 一.循环引用的本质 //代码——ARC环境 void test1() { Person *per = [[Person alloc] init]; per.age = 10; per.block = ^{ NSLog(@"-------1"); }; } int main(int argc, const char * argv[]) { @autoreleasepool { test1(); // test2(); } NSLog(

iOS Block循环引用

前言 本篇文章精讲iOS开发中使用Block时一定要注意内存管理问题,很容易造成循环引用.本篇文章的目标是帮助大家快速掌握使用block的技巧. 我相信大家都觉得使用block给开发带来了多大的便利,但是有很多开发者对block内存管理掌握得不够好,导致经常出现循环引用的问题.对于新手来说,出现循环引用时,是很难去查找的,因此通过Leaks不一定能检测出来,更重要的还是要靠自己的分析来推断出来. 声景一:Controller之间block传值 现在,我们声明两个控制器类,一个叫ViewContr