NSMutableArray содержит объекты и управление памятью - PullRequest
2 голосов
/ 29 апреля 2011

Я изучаю управление памятью и создал небольшую программу, которая управляет табличным объектом.

Вот мой .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 ]

3 голосов
/ 29 апреля 2011

Массив не копирует добавленные в него объекты, он просто сохраняет их.У вас есть только один объект Person для области действия метода -clic2:.Вы устанавливаете данные и добавляете их в массив.Массив хранит указатель на объект Person и увеличивает его счетчик сохранения.Но у вас все еще есть только один объект Person, поэтому любое изменение имени или фамилии перезапишет заданные вами исходные данные.В конце вашего -clic2: метода в массиве есть две ссылки на один и тот же объект Person.

Что вам нужно, это примерно так:

3 голосов
/ 29 апреля 2011

Вы создаете только один экземпляр лица и изменяете его несколько раз (перезаписывая значения в процессе). Добавление объекта в массив не создает его копию. Он поддерживает только ссылку на этот объект в массиве.

Вот пример того, что может сделать ваш код -

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];
2 голосов
/ 29 апреля 2011

Это происходит потому, что у вас есть только один Person объект.

Когда вы делаете это:

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

pers указывает на блок памяти, содержащий объект Person; он содержит местоположение или адрес . Это «указатель на человека». Когда вы делаете это:

[myarray addObject:pers];

массив не копирует ни один из этих блоков памяти; все, что он делает, это копирует местоположение , которое обозначено pers. * Когда вы изменяете значения переменных pers, он изменяет память в том же блоке. У вас все еще есть Person объект в том же месте, но с другим содержимым. При добавлении pers в массив снова:

[myarray addObject:pers];    // Second time

массив просто делает еще одну копию указателя , который все еще имеет только то же местоположение, и поэтому массив имеет две копии указателя; два одинаковых адреса. Поэтому, когда вы смотрите на значения по этим адресам, вы видите одинаковые значения, потому что на самом деле это просто один объект.


* Это также означает, что он заинтересован в сохранении этой памяти, вызывая retain для объекта в этом месте.

2 голосов
/ 29 апреля 2011

в вашем коде

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

Вы добавляете один и тот же объект в ваш массив дважды, в массиве хранится указатель на ваш объект Person pers, когда вы setFirstName: и setLastName во второй раз делаете это для того же объекта в памяти, Ваш указатель такой же. Когда вы вызываете addObject: он просто собирается увеличить счетчик сохранения этого объекта и сохранить его указатель, он не создает его копию.

Создайте второй объект Person с именем pers2 и выполните следующие действия.

[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
}
1 голос
/ 29 апреля 2011

Да, ты прав. 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];
...