NSMutableArray包含对象和内存管理

时间:2011-04-29 02:42:36

标签: objective-c arrays memory-management 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:感谢所有内存管理解释

5 个答案:

答案 0 :(得分:3)

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

以下是您的代码可以执行的操作示例 -

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

答案 1 :(得分:3)

数组不会复制添加到它的对象,它只是保留它们。对于-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 ];
}

答案 2 :(得分:2)

在您的代码中

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

您正在向数组中添加两次相同的对象,当您perssetFirstName:第二次执行时,数组会存储指向Person对象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
}

答案 3 :(得分:2)

这种情况正在发生,因为您只有一个Person对象。

执行此操作时:

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

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

[myarray addObject:pers];

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

[myarray addObject:pers];    // Second time

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


*它还表明它有兴趣通过在该位置的对象上调用retain来保持该内存。

答案 4 :(得分:1)

是的,你是对的。 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];