假设有一个具有以下界面的类:
#import <Foundation/Foundation.h>
@interface MyClass : NSObject {
}
@property (nonatomic, retain) NSDate* myDate;
-(void)foo;
@end
以及以下实施:
#import "MyClass.h"
@implementation MyClass
@synthesize myDate = _myDate;
- (void)dealloc
{
[_myDate release];
[super dealloc];
}
-(void)foo
{
NSDate* temp = [[NSDate alloc] init];
self.myDate = temp;
[temp release];
}
@end
1)函数foo
会像这样释放,确保正确维护对象的保留计数(即没有内存泄漏,也没有执行不必要的释放)。
NSDate* temp = [[NSDate alloc] init];
self.myDate = temp;
[temp release];
2)与1)相同的问题,除了适用于以下技术:
self.myDate = [[NSDate alloc] init];
[self.myDate release]
3)与1)相同的问题,除了适用于以下技术:
self.myDate = [[NSDate alloc] init] autorelease];
4)与1)相同的问题,但适用于以下技术:
self.myDate = [[NSDate alloc] init];
[_myDate release]
5)与1)相同的问题,但适用于以下技术:
[_myDate release];
_myDate = [[NSDate alloc] init];
答案 0 :(得分:7)
1)很好。
2)可能不安全,并将在最新的LLVM静态分析仪中触发警告。这是因为getter方法返回的对象可能与传递给setter的对象不同。 (例如,setter可能已制作副本,或者可能验证失败并改为设置nil
。)这意味着您正在泄漏原始对象并过度释放吸气剂给您的那个。
3)很好;类似于1,但是当当前自动释放池被耗尽而不是立即释放时将会发布。
4)可能不安全,但不会触发我所见过的警告。问题类似于2中描述的问题; ivar中的对象可能不是你传递给setter的对象。
5)安全,但不会使用setter方法或通知任何观察者的财产。
如果属性是retain
类型,并且getter和setter都只是合成版本,则上述所有示例都可以使用。但是,它们并不都代表最佳实践,可能会触发分析警告。目标应该是-foo
方法无论 myDate
如何管理其内存,都能正常工作。您上面的一些示例不会这样做。
例如,如果您决定稍后将属性更改为copy
,则不应要求您更改任何其他代码以使其正常工作。在情况2和4中, 需要更改其他代码,因为foo
方法假定setter始终成功并始终设置原始对象。
答案 1 :(得分:3)
5)是一个错误 - 它泄漏旧实例,因为它没有被释放但只是重新分配。
1)干净,是最好的方法。 4)没问题但是给内存系统带来了一些负担 - 对象的寿命可能超过需要的时间。 2)技术上还可以,但你不应该直接保留/释放财产 - 这就是语法糖的用途! 3)技术上还可以,但也绕过了属性并依赖于实现细节。
当部分代码发生变化时,不鼓励2)和3)并在将来遇到麻烦。
编辑:新代码不泄漏5)。但它有同样的缺点。
我们得到了对属性的支持是有原因的,并且它具有很好的一致性。你应该只考虑绕过它们,如果你的时间曲线给出了非常明确的提示,这是瓶颈(不太可能)。
答案 2 :(得分:2)
首先,如果你想避免使用alloc,release,autorelease等...你可以调用一个不以alloc开头的日期工厂方法。
例如: self.myDate = [NSDate date];
日期类工厂方法根据约定规则执行自动释放。然后酒店保留它。
Alloc会给它一个保留计数为1,然后分配属性将保留它。由于您的班级现在正从该财产中保留它,您可以将其释放以反击该行为。
同上,但这是一个关于如何做到这一点的奇怪回合。
3相当于我上面的代码([NSDate date]);
在这种情况下,属性将保留它(在alloc增加保留计数之后),然后你将在封面下减少它。有效,但我不建议这样做,因为你合成(保留)财产会为你做。
答案 3 :(得分:-1)
释放和更新的模式仅仅是一种语义。您将获得以下各项的保留计数。
myObject = [Object alloc]
objectCopy = [myObject copy]
myNewObject = [Object newObjectWithSomeProperties:@"Properties"] // Keyword here being new
// And of course
[myObject retain]
具有修饰符(retain)或(copy)的属性将保留计数。 后备存储_myDate仅仅是实际存储对象的位置。
当你得到保留计数时,你需要释放。 立即使用[myObject release]消息或让池使用[myObject autorelease]
释放它无论如何,都需要释放任何保留(隐式或显式)的保留。无论如何,垃圾收集器不会收集你的对象而你会有内存泄漏。
中最常见的用法
Object myObject = [[[Object alloc] init] autorelease]; // Use this when you dont plan to keep the object.
Object myObject = [[Object alloc] init];
self.myProperty = [myObject autorelease]; // Exactally the same as the Previous. With autorelease
// Defined on the assignment line.
self.myProperty = [[[Object alloc] init] autorelease]; // Same as the last two. On one line.
我将展示其他可能性
// Uncommon. Not incorrect. But Bad practice
myObject = [[Object alloc] init];
self.myProperty = myObject;
// Options
[_myProperty release] // Bad practice to release the instance variable
[self.myProperty release] // Better practice to Release the Property;
// releasing the property or the instance variable may not work either.
// If your property is using the (copy) modifier. The property is copied rather then retained.
// You are still given a retain count.
// But calling release on a copy will not release the original
[myObject release]; // Best Practice. Good when Threading may run the autorelease pool
[myObject autorelease]; // As good as the previous.
// But may leave your object in memory during long operations
基本上,给定retain的对象将是属性变量和实例变量中的同一对象。释放它们中的任何一个都会释放它。 然而。最佳实践说,如果你保留一个对象。最好在该对象的同一变量上调用release。即使在另一方调用Autorelease和retain。
//为您提供保留计数的其他项目。 核心媒体或核心任何在名称中创建或复制的函数。