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

iPhone内存管理(附具体示例/问题),iphone,objective-c,ios,memory-management,Iphone,Objective C,Ios,Memory Management,大家好。我知道有人问过这个问题,但我仍然不清楚Objective-C中的内存管理。我觉得我已经很好地掌握了它,但我仍然希望以下代码有一些正确的答案。我有一系列的例子,我希望有人能澄清 设置实例变量的值: 假设我有一个NSMutableArray变量。在我的类中,当我初始化它时,是否需要调用它上的retain 是吗 fooArray = [[[NSMutableArray alloc] init] retain]; 或 执行[[NSMutableArray alloc]init]是否已将reta

大家好。我知道有人问过这个问题,但我仍然不清楚Objective-C中的内存管理。我觉得我已经很好地掌握了它,但我仍然希望以下代码有一些正确的答案。我有一系列的例子,我希望有人能澄清

设置实例变量的值

假设我有一个
NSMutableArray
变量。在我的类中,当我初始化它时,是否需要调用它上的
retain

是吗

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

执行
[[NSMutableArray alloc]init]
是否已将
retain
计数设置为1,这样我就不需要对其调用
retain
?另一方面,如果我调用了一个方法,我知道该方法返回一个
autorelease
d对象,那么我肯定必须对它调用
retain
,对吗?像这样:

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

我询问
retain
,因为我对
@property
的自动setter的工作原理有点困惑

如果我将
fooArray
设置为带有
retain
集的
@属性
,Objective-C将自动创建以下setter,对吗

- (void)setFooArray:(NSMutableArray *)anArray {
    [fooArray release];
    fooArray = [anArray retain];
}
所以,如果我有这样的代码:
self.fooArray=[[NSMutableArray alloc]init]
(我认为这是有效的代码),Objective-C创建了一个setter方法,该方法对分配给
fooArray
的值调用
retain
。在这种情况下,
retain
计数实际上是2吗

设置属性值的正确方法

我知道在这方面和(可能)辩论中存在一些问题,但设置
@属性的正确方法是什么

这个

还是这个

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

我想澄清一下这些例子。谢谢

首先,这一行:

fooArray = [[NSMutableArray alloc] init];
fooArray的保留计数将自动为1

第二,是的,是2。您对setter实现的猜测是正确的

第三,后者是正确的

根据苹果的说法,任何以
alloc
new
开头的方法,或包含
copy
的方法,都属于调用方

要获得对象的所有权,您必须
保留它

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

正确的方法是:

fooArray = [[NSMutableArray alloc] init];
由于
autorelease
对象属于当前自动释放池,因此必须对其调用
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
,这是由属性设置器方法设置的
保留
。)

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

self.fooArray = ...;
而不是

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

- (void)setFoo:(NSArray *)aFoo {
    if (foo == aFoo) {
        return;
    }
    NSArray *oldFoo = foo;
    foo = [aFoo retain];
    [oldFoo release];
}
在执行类似操作时(假设保留了foo),实例变量的retain计数为2是正确的:

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

// 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实现文件中将它们声明为类扩展名。举个例子,考虑一个简单的对象对象,它有一个名称,和一个布尔属性<代码> Debug Save>代码>,它简单地指示对象是否被保存到某个数据库中。由于我们不想将其公开给外界,但仍将属性的好处保留在实现文件中,因此我们可以创建头文件,该文件将包含所有实例变量(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

我知道创建临时数组的对象来分配属性数组很好,但是self.fooArray=[[NSMutableArray alloc]init]有什么问题;为什么它会出现内存泄漏。@Ishu Gupta,这里有一个内存泄漏,因为您已经拥有了该对象,因为您在该行调用了
alloc
,并且您的
@属性
setter可能有
retain
修饰符,所以该赋值的最终结果变成
self.fooArray=[[NSMutableArray alloc]init]retain]
,因此泄漏。@Ishu Gupta我很高兴能帮上忙:)这真是一个很好的解释。谢谢你,雅各布。我的一些疑问现在已经清楚了。谢谢你的回答,阿努拉格。当我不需要其他类可以访问变量时,我一直试图避免使用属性。有点像n00b问题,但是。。。我可以拥有其他类无法访问的属性吗?谢谢你的编辑,Anurag。如果你不介意的话,我想问几个问题。。能否在.m的@interface中声明canSave变量?在我看来,未公开的变量已经是隐式私有的,因此必须将其标记为私有似乎有点多余。我听到您提到了私有实例变量部分,但不幸的是,这是我们可以通过Objective-C将实现细节保持私有的最接近方法。如果我们不将变量标记为私有,然后默认情况下,它们具有受保护的访问权限,这可能是需要的,也可能不是需要的。
// 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];
// 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