释放财产的可接受方式

时间:2011-09-28 01:39:51

标签: objective-c ios

假设有一个具有以下界面的类:

#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];

4 个答案:

答案 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];

日期类工厂方法根据约定规则执行自动释放。然后酒店保留它。

  1. Alloc会给它一个保留计数为1,然后分配属性将保留它。由于您的班级现在正从该财产中保留它,您可以将其释放以反击该行为。

  2. 同上,但这是一个关于如何做到这一点的奇怪回合。

  3. 3相当于我上面的代码([NSDate date]);

  4. 在这种情况下,属性将保留它(在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。

//为您提供保留计数的其他项目。 核心媒体或核心任何在名称中创建或复制的函数。