NSMutableArray包含的对象和内存管理

问题描述:

我正在研究内存管理,并且我做了一个管理表对象的小程序。NSMutableArray包含的对象和内存管理

这里是我的.h:

@interface Person : NSObject { 
     NSString *firstName; 
     NSString *lastName; 
} 

@property (readwrite, copy) NSString *firstName; 
@property (readwrite, copy) NSString *lastName; 

这里是我的.m:

- (IBAction)clic2:(id)sender { 
    Person *pers = [[Person alloc]init]; 
    NSMutableArray *myarray = [[NSMutableArray alloc] init]; 

    [pers setFirstName:@"FN 1"]; // = pers.firstName 
    [pers setLastName:@"LN 1"]; // = pers.lastName 
    [myarray addObject:pers]; 

    [pers setFirstName:@"FN 2"]; 
    [pers setLastName:@"LN 2"]; 
    [myarray addObject:pers]; 

    [pers release]; 


    for(int i = 0; i < [myarray count]; i++) 
     { 
     pers = [myarray objectAtIndex:i]; 
     NSLog(@"%d %@ %@ %@", [myarray objectAtIndex:i], pers, pers.firstName, pers.lastName); 
     } 

} 


- (void)dealloc 
{ 
    [firstName release]; 
    firstName = nil; 

    [lastName release]; 
    lastName = nil; 

    [super dealloc]; 
} 

,这是我的NSLog

 
2011-04-28 21:40:11.568 temp[4456:903] 4495296 <Person: 0x1004497c0> FN 2 LN 2 
2011-04-28 21:40:11.571 temp[4456:903] 4495296 <Person: 0x1004497c0> FN 2 LN 2 

正如你所看到的,所存储的信息是一样的;这似乎是因为myarray总是存储阵列中人员的相同内存地址/内容。

我明白,当我在第二次调用pers的内容时,系统也会更改myarray中的信息。

我该如何做以免覆盖数据?

谢谢你的回答。


编辑:本例是2人,但这个想法是管理给出一个无限数量


EDIT2:谢谢一切都在这种情况下

该数组不会复制添加到它的对象,它只是保留它们。对于-clic2:方法的范围,您只有一个Person对象。您设置数据并将其添加到数组中。该数组存储一个指向Person对象的指针并增加其保留计数。但是,您仍然只有一个Person对象,因此对首字母或姓氏的任何更改都会覆盖您设置的原始数据。在-clic2:方法结尾的数组中有两个对同一Person对象的引用。

你需要的是这样的:

- (IBAction)buttonClicked: (id)sender 
{ 
    Person   *person; 
    NSMutableArray *array; 
    NSInteger   i, count = 10; 

    array = [[ NSMutableArray alloc ] initWithCapacity: count ]; 
    for (i = 1; i <= count; i++) { 
     /* create a new Person object on each iteration through the loop */ 
     person = [[ Person alloc ] init ]; 
     /* person has a -retainCount of at least +1 after allocation */ 

     person.firstName = [ NSString stringWithFormat: @"FN %d", i ]; 
     person.lastName = [ NSString stringWithFormat: @"LN %d", i ]; 

     /* adding the object increments the person retainCount to at least +2 */ 
     [ array addObject: person ]; 

     /* 
     * -release decrements the retainCount, so when we release the array 
     * at the end of the method and it sends release to all its objects 
     * the memory allocated for the Person objects will be reclaimed. 
     */ 
     [ person release ]; 
    } 

    for (person in array) { 
     NSLog(@"%@ %@ %@", person, person.firstName, person.lastName); 
    } 

    /* releasing the array also sends -release to all its objects */ 
    [ array release ]; 
} 
+0

谢谢,有一个问题,这是这样做的常见做法? – Vyggo 2011-04-29 03:25:47

+0

当然。想象一下,如果你正在逐行阅读包含名字和姓氏的文本文件。您仍然需要一个唯一的Person对象来存储从每行文本读取的数据。 – 2011-04-29 03:33:42

+0

工作正常。谢谢。 – Vyggo 2011-04-29 03:47:46

内存管理的解释在您的代码

[pers setFirstName:@"FN 1"]; // = pers.firstName 
[pers setLastName:@"LN 1"]; // = pers.lastName 
[myarray addObject:pers]; 

[pers setFirstName:@"FN 2"]; 
[pers setLastName:@"LN 2"]; 
[myarray addObject:pers]; 

您正在向您的数组添加相同的对象两次,数组是stori ng指向你的Person对象的指针pers当你第二次为内存中的同一对象做第二次setFirstName:setLastName时,你的指针是一样的。当你调用addObject时:它只会增加该对象的保留计数并存储它的指针,而不会创建它的副本。

创建名为pers2的第二个Person对象并执行以下操作。

[pers1 setFirstName:@"FN 1"]; // = pers.firstName 
[pers1 setLastName:@"LN 1"]; // = pers.lastName 
[myarray addObject:pers1]; 

[pers2 setFirstName:@"FN 2"]; 
[pers2 setLastName:@"LN 2"]; 
[myarray addObject:pers2]; 

另外不要忘记当你完成时释放myarray,目前你正在泄漏该对象。

另一个需要注意的是,你应该使用快速枚举来迭代你的数组。

for(Person *person in myarray) { 
    // your code 
} 

您只创建一个人的实例并多次修改它(覆盖进程中的值)。将一个对象添加到数组不会创建它的副本。它只维护对该数组中该对象的引用。

这里是你的代码可以做一个例子 -

NSMutableArray *myarray = [[NSMutableArray alloc] init]; 

Person *pers1 = [[Person alloc]init]; 
[pers1 setFirstName:@"FN 1"]; // = pers1.firstName 
[pers1 setLastName:@"LN 1"]; // = pers1.lastName 
[myarray addObject:pers1]; 
[pers1 release]; 

Person *pers2 = [[Person alloc]init]; 
[pers2 setFirstName:@"FN 2"]; 
[pers2 setLastName:@"LN 2"]; 
[myarray addObject:pers2]; 
[pers2 release]; 

这是发生,因为你只能有一个Person对象。

当你这样做:

Person *pers = [[Person alloc] init]; 

pers持有一个Person对象的内存块;它包含一个位置,或者地址。这是一个“指向人的指针”。当你这样做:

[myarray addObject:pers]; 

该阵列不复制任何内存块;它所做的就是复制的位置,它由pers表示。*当您更改pers的变量值时,它会更改同一块中的内存。您仍然在同一位置有Person对象,但内容不同。当您再次添加pers到数组:

[myarray addObject:pers]; // Second time 

数组只是使指针,仍然只具有相同的位置,所以数组指针的两个副本的另一个副本;两个地址是相同的。因此,当您查看这些地址处的值时,您会看到相同的值,因为它实际上只是一个对象。


*这也表明,它感兴趣的周围保持该内存通过在位置调用对象的retain

是的,你说得对。 NSMutableArray将指针存储到Person对象,而不是对象本身/副本。

如果你真的想要复制最好的东西,你可以做的是让Person类实现NSCopying协议。

在.H添加NSCopying声明,保留其余相同

@interface Person : NSObject <NSCopying> { 
... 
} 

.M(和以前一样,但是追加此方法)

- (id)copyWithZone:(NSZone *)zone { 
    Person *objectCopy = [[Person allocWithZone:zone] init]; 
    objectCopy.firstName = self.firstName; 
    objectCopy.lastName = self.lastName; 
    return [objectCopy autorelease]; 
} 

那么你可以做:

Person *pers = [[Person alloc]init]; 
NSMutableArray *myarray = [[NSMutableArray alloc] init]; 

[pers setFirstName:@"FN 1"]; // = pers.firstName 
[pers setLastName:@"LN 1"]; // = pers.lastName 
[myarray addObject:[pers copy]]; 

[pers setFirstName:@"FN 2"]; 
[pers setLastName:@"LN 2"]; 
[myarray addObject:[pers copy]]; 

[pers release]; 
+0

感谢的,做工精细。一个问题:如果每次创建新指针时,为什么要使用allocWithZone而不是Alloc? – Vyggo 2011-04-29 03:50:26