Tag: memory management
为什么要在addsubview:一个view对象后,release它
by Elton on 十.24, 2009, under iPhone
先看代码:
1 2 3 | IMGView *imgView = [[IMGView alloc] initWithFrame:CGRectMake(10, 0, 300, 300)]; [self.view addSubview:imgView]; [imgView release]; |
为什么imgView要release呢?可能很多人跟我一样,之前不是很清楚。 我们逐行分析一下
第一行,alloc一次,imgView对象retainCount为1,
第二行,addSubview一次,此方法会把你传过去的对象retain一次,那么此时它的retainCount为2。self.view变为它的第二个待有者。参考:The receiver retains view. If you use removeFromSuperview to remove view from the view hierarchy, view is released.
第三行,调用release方法,此处释放对imgView的所有权,retainCount减1。
到语言句尾imgView的所有者只剩下self.view,并且它的retainCount仅为1。内存管理第一铁则,谁retain(alloc,copy)谁release(autorelease)。上述的做法也是为了符合这一准则。
如何获得对象的retain count
by Elton on 十.15, 2009, under Mac
Mac特别是iPhone中的内存管理是通过引用计数来实现的。 而对于开发者来说,特别是从具备垃圾回收功能的语言开发工程师来说,这种内存管理方式具有很大的挑战性。
我们最关心的就是一个对象的“retain count”, 当这个数字变成0的时候,这个对象就将被释放内存,如果此时尝试访问这个对象,你的应用程序就会崩溃。但是如果你不及时release对象,将retain count数量减少的话,又会造成内存泄漏。
想知道一个对象的retain count的值的方法其实很简单。
1 | NSLog([NSString stringWithFormat:@"Retain Count:%i", [someObject retainCount]]); |
对象的retainCount方法就会反回这个对象的retain count的值。
Cocoa内存管理的简单规则[翻译]
by Elton on 六.25, 2009, under Mac
看了一篇mmalcolm crawford写的文章,觉得不错, 原文在此。比较清楚的讲解了Cocoa的内存管理。 对于Mac和iPhone的开发有很大帮助。 特翻译并略做修改以方便理解,希望与大家共勉
对于一个新的Cocoa开发者来说,刚接触到内存管理的时候,一定很困惑。 下面给出了一些简单的规则,可以让你舒服些。如果你没有很好的使用这些规则的话,通常会带来内存泄露的问题或者运行时的异常。
Cocoa过去没有垃圾回收机制,iPhone现在也没有。所以你必须自己来通过-retain, -release and -autorelease这些命令使用引用计数(reference counting)技术来管理内存。
| 方法 | 描述 |
|---|---|
| -retain | 给一个对象的引用计数加1 |
| -release | 给一个对象的引用计数减1 |
| -autorelease | 在将来的某些时候将一个对象的引用计数减1 |
| -alloc | 分配一块内存给对象,引用计数器将设为1 |
| -copy | 拷贝一个对象,将返回引用计数为1的一个对象 |
引用计数规则
在你的方法中使用了-copy,-alloc和-retain来申请内存,就要对应的使用-release和-autorelease来释放内存;- 当对象使用便捷方法创建的时候(如:NSString类的 stringWithString方法),则这个对象将被视为已经使用了
autorelease,在将来某时自动释放内存; - 如果你定义了实例变量,则在你的类中实现
-dealloc这个方法来释放他们。
例子:
-alloc / -release
1 2 3 4 5 6 7 8 | - (void)printHello { NSString *string; string = [[NSString alloc] initWithString:@"Hello"]; NSLog(string); // 我们使用alloc来创建了一个string 所以要release它 [string release]; } |
便捷构造方法
1 2 3 4 5 6 7 8 | - (void)printHello { NSString *string; string = [NSString stringWithFormat:@"Hello"]; NSLog(string); // 我们构建这个string的时候,使用了便捷构造方法( convenience constructor ) // 所以我们认为它是 autoreleased的 } |
永远使用accessor方法
有时候你会觉得使用accessor方法会比较教条和无聊,但是使用他们会降低内存泄露的机会。
如果对于实例变量你也使用 -retain 和-release 来管理内存的话,那就错了。
例子
在接口中定义了一个实例变量
在实现中加入accessor方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | - (NSNumber *)count { return count; // 不需要retain或者release // 我们只是返回这个变量的值 } - (void)setCount:(NSNumber *)newCount { // 如果每个人都使用上述同样的内存管理规则 // 我们就得认为newCount是autoreleased的。 // 因为我们想使用这个变量,所以我们必须retain它 // 避免它被释放掉。 [newCount retain]; // 因为我们只想在这个方法中改变这个类中的count值 // 所以要通过这个方法,先释放掉之前的内存 // 在Objective-C中[nil release]也是被允许的 // 我们必须将次调用放在 [newCount retain] 的后面 // 因为如果当count和newCount都指向同一个对象的时候 // 我们会错误的释放它 [count release]; // 关联新的引用 count = newCount; } |
因为我们的类有实例变量,所以需要实现-dealloc方法来释放内存。
1 2 3 4 5 | - (void)dealloc { [self setCount:nil]; [super dealloc]; } |
假设我们需要一个重置count的值的方法,我们有两个选择
便捷构造方法convenience constructor
使用-alloc方法
1 2 3 4 5 6 | - (void)reset { NSNumber *zero = [[NSNumber alloc] initWithInt:0]; [self setCount:zero]; [zero release]; } |
常见错误
下面的问题在一些简单的环境下可能会正常工作,但是避开使用accessor方法,在某些时候几乎可以肯定会带来内存泄露的问题。
没有使用accessor方法
1 2 3 4 5 6 | - (void)reset { NSNumber *zero = [[NSNumber alloc] initWithInt:0]; [count release] count = zero; } |
变量没有释放
1 2 3 4 5 | - (void)reset { NSNumber *zero = [[NSNumber alloc] initWithInt:0]; [self setCount:zero]; } |
当使用alloc创建一个新对象的时候,retain count是1,如果我们没有在这个方法中使用-release方法,这个number对象将永远不能被释放,将会造成内存泄露。
已经释放内存的变量再次调用release
1 2 3 4 5 6 | - (void)reset { NSNumber *zero = [NSNumber numberWithInt:0]; [self setCount:zero]; [zero release]; } |
这将导致当你第一次访问count的时候发生错误。 便捷构造方法将返回一个autoreleased对象,所以你不必使用release。 在使用autoreleased后这样做,将减少count的计数到0,这个对象将被释放。当你之后视图访问count的时候,你将向一个自由对象(free object)发送调用消息(message),通常你将得到一个SIGBUS 10的错误。
经常让人混淆迷惑的问题 - 数组和其他集合类
当一个对象被添加到一个array, dictionary, 或者 set等这样的集合类型中的时候,集合会retain它。 对应的,当集合类被release的时候,它会发送对应的release消息给包含在其中的对象。 因此,如果你想建立一个包含一堆number的数组,你可以像下面示例中的几个方法来做
1 2 3 4 5 6 7 8 | NSMutableArray *array; int i; // ... for (i = 0; i < 10; i++) { NSNumber *n = [NSNumber numberWithInt: i]; [array addObject: n]; } |
在这种情况下, 我们不需要retain这些number,因为array将替我们这么做。
1 2 3 4 5 6 7 8 9 | NSMutableArray *array; int i; // ... for (i = 0; i < 10; i++) { NSNumber *n = [[NSNumber alloc] initWithInt: i]; [array addObject: n]; [n release]; } |
在这个例子中,因为你使用了-alloc去建立了一个number,所以你必须显式的-release它,以保证retain count的平衡。因为将number加入数组的时候,已经retain它了,所以数组中的number变量不会被release


