iPhone内存管理(具体示例/问题)

时间:2010-12-28 05:31:10

标签: iphone objective-c ios memory-management

嘿所有人。我知道这个问题已被问到,但我仍然没有清楚地了解Objective-C中的内存管理。我觉得我对它有很好的把握,但是我仍然希望得到以下代码的正确答案。我有一系列的例子,我喜欢让某人澄清。

设置实例变量的值

说我有一个NSMutableArray变量。在我的课堂上,当我初始化它时,我是否需要在其上调用retain

我做

fooArray = [[[NSMutableArray alloc] init] retain];

fooArray = [[NSMutableArray alloc] init];

执行[[NSMutableArray alloc] init]是否已将retain计数设置为1,因此我无需在其上调用retain?另一方面,如果我调用一个我知道返回autorelease d对象的方法,我肯定会在其上调用retain,对吧?像这样:

fooString = [[NSString stringWithFormat:@"%d items", someInt] retain];

属性

我问retain,因为我对@property自动设置器的工作原理有点困惑。

如果我将fooArray设置为@property并设置retain,则Objective-C会自动创建以下设置器,对吧?

- (void)setFooArray:(NSMutableArray *)anArray {
    [fooArray release];
    fooArray = [anArray retain];
}

所以,如果我有这样的代码:self.fooArray = [[NSMutableArray alloc] init];(我相信它是有效代码),Objective-C会创建一个setter方法,在分配给retain的值上调用fooArray 。在这种情况下,retain计数实际上是2吗?

设置属性值的正确方法

我知道对此和(可能)辩论有疑问,但这是设置@property的正确方法吗?

此?

self.fooArray = [[NSMutableArray alloc] init];

还是这个?

NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];

我想对这些例子做一些澄清。谢谢!

3 个答案:

答案 0 :(得分:4)

根据Apple的Object Ownership Policy,任何以allocnew开头,或包含copy的方法都由来电者拥有。

要获取对象的所有权,您必须retain

因此,在您的第一个示例中,retain是不必要的,因为您已拥有该对象。

正确的方法:

fooArray = [[NSMutableArray alloc] init];

由于autoreleased个对象归当前自动释放池所有,因此您必须对它们调用retain以获得它们的所有权,因此这个示例是正确的:

fooString = [[NSString stringWithFormat:@"%d items", someInt] retain];

这也可以正常工作:

self.fooString = [NSString stringWithFormat:@"%d items", someInt]; //retained by property setter

对于使用属性设置器的最后一个示例,这将是正确的方法:

NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];

我建议使用以下解决方案,而不是必须执行上述操作:

self.fooArray = [NSMutableArray arrayWithCapacity:10];

arrayWithCapacity:将返回自动释放的NSMutableArray,即属性设置器方法的retain。 :)

答案 1 :(得分:3)

理想情况下,您可能希望尽可能使用访问器,尤其是在处理对象时,因为它们有助于避免许多内存问题。所以即使是实例变量,也应该这样做:

self.fooArray = ...;

而不是

fooArray = ...;

您应该为对象实例变量声明属性的原因是因为内存管理稍微复杂一些,并且每次手动重新创建它都很棘手。非原子保留属性的正确设置器如下所示:

- (void)setFoo:(NSArray *)aFoo {
    if (foo == aFoo) {
        return;
    }
    NSArray *oldFoo = foo;
    foo = [aFoo retain];
    [oldFoo release];
}

当你做这样的事情时(保留foo被保留),你对保留计数为2的实例变量是正确的:

self.foo = [[NSMutableArray alloc] init];

第一个保留计数来自alloc,第二个来自合成二传手。其中任何一个都应该有效:

// longer, explicit version, releases immediately (more efficient)
NSMutableArray *aFoo = [[NSMutableArray alloc] init];
self.foo = aFoo;
[aFoo release];

// autoreleased, not so bad unless you're a memory management freak
self.foo = [[[NSMutableArray alloc] init] autorelease];

// an even shorter version of the above
self.foo = [NSMutableArray array];

要创建私有属性,可以将它们声明为.m实现文件中的类扩展。举一个例子,考虑一个简单的Person对象,它有一个名称,以及一个布尔属性didSave,它只是指示对象是否已保存到某个数据库。既然我们不想将这个暴​​露给外界,但仍然保留了实现文件中属性的好处,我们可以创建头文件将所有实例变量(public,private,protected)和只有公共属性:

// Person.h

@interface Person {
    NSString *name;

    @private
    BOOL didSave;
}

@property (nonatomic, retain) NSString *name;

@end

但是在实现中声明私有属性:

// Person.m

// property is declared as a class extension, making it 
// invisible to the outside world.
@interface Person ()
    @property BOOL didSave;
@end

@implementation

// synthesize as normal
@synthesize name, didSave;

@end

答案 2 :(得分:0)

首先,用这一行:

fooArray = [[NSMutableArray alloc] init];

fooArray将自动保留计数为1。

第二,是的,它是2.你对setter实现的猜测是正确的。

第三,后者是对的