Утечка памяти для NSDictionary, загруженного файлом plist - PullRequest
2 голосов
/ 22 мая 2010

У меня проблема утечки памяти, которую просто не могу понять!Посмотрите этот метод инициализации:

- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {

if (self = [super init]) {

    NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
    NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
                                [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                                objectForKey:nomeCompositore]
                                objectForKey:nomeOpera]];

    self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
    self.compositore = [[NSString alloc] initWithString:nomeCompositore];
    self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}

return self;}

Тогда это небольшое изменение (обратите внимание на self.tipologia):

- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {

if (self = [super init]) {

    NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
    NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
                                [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                                objectForKey:nomeCompositore]
                                objectForKey:nomeOpera]];

    self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
    self.compositore = [[NSString alloc] initWithString:nomeCompositore];
    self.tipologia = [[NSString alloc] initWithString:@"Test"];
}

return self;}

В первом варианте генерируется утечка памяти, во втором нет!И я просто не могу понять почему!Утечка памяти подтверждается приборами, выделившими строку:

[NSDictionary dictionaryWithContentsOfFile:pathOpere]

Это метод dealloc:

- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}

Ответы [ 2 ]

1 голос
/ 22 мая 2010

Помните, что alloc возвращает принадлежащий вам объект.

Если вы объявили свои три строковых свойства как retain, назначение этих объектов вашим свойствам означает, что теперь вы владеете каждым из них дважды - один раз, потому что вы его заблокировали, и снова, потому что вы присвоили его своему свойству. Объекты остаются живыми, потому что ничто не освобождает их вторых владельцев.

Если вы объявили свойства как copy (что является правильным способом объявления свойства NSString), то при назначении объекта там сохраняется копия в качестве значения свойства. Вы больше ничего не делаете с оригинальными объектами, которые остаются живыми, потому что ничто не освобождает их.

В любом случае, это ваша утечка.

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

Вы не должны использовать доступ к собственности здесь. Помните, что присвоение свойству - это сообщение set<PropertyName>:, и ваш объект еще не полностью инициализирован. Отправка сообщения не полностью инициализированному или не полностью освобожденному объекту вызывает проблемы, особенно когда задействованы подклассы, поскольку они могут переопределять методы доступа так, как этого не ожидает суперкласс.

Таким образом, только в init присваивайте непосредственно переменным экземпляра. Только в dealloc отправляйте release сообщения непосредственно объектам в переменных экземпляра. Везде, где угодно, используйте доступ к свойствам.

Вы также не должны использовать alloc и initWithString: здесь. Это будет работать, но соглашение состоит в том, чтобы отправлять copy сообщений объектам, которые у вас уже есть, так же, как и свойства. Отправьте copy сообщения вашим входным строковым объектам, затем назначьте копии переменным вашего экземпляра.

Когда вы используете доступ к свойству, используйте вспомогательные конструкторы (например, stringWith…:), так как они возвращают объекты, которыми вы не владеете. Когда вы присваиваете эти объекты вашим объявленным copy свойствам, вы фактически будете хранить копии, которые у вас есть.

Другой способ - использовать alloc и initWithWhatever:, а затем немедленно autorelease этот объект, прежде чем присваивать его свойству; таким образом создается объект, которым вы владеете, а затем сразу же отказывается от владения, прежде чем назначить его свойству.

0 голосов
/ 22 мая 2010

Попробуйте

nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];

или

self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];

вместо self.xxx = [[yyy alloc] init...].


В исходном коде RHS присвоения возвращает объект со счетом удержания +1, и если вы сделаете @property с (retain) или (copy), окончательный счет удержания будет +2. Поэтому, даже если вы отпустите их в -dealloc, счетчик сохраняемых данных будет равен +1, что приведет к утечке памяти.


Кстати, нет смысла звонить +dictionaryWithDictionary:. Просто используйте

NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                            objectForKey:nomeCompositore]
                            objectForKey:nomeOpera];
...