Каким образом релиз обрабатывается для сохраняемых свойств @synthesized?

У меня есть некоторые вопросы о синтезированных свойствах в Objective-C. Полный список следует, но основной вопрос заключается в следующем: Как компилятор гарантирует, что ivars для синтезированных свойств будут правильно выпущены, хотя мой код может включать или не включать методы выпуска в dealloc?

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

Несколько похожих вопросов:


Настройка: Рассмотрим класс с единственным свойством:

@interface Person : NSObject
{
    NSString * name;
}
@property (nonatomic, retain) name;
@end

Вопрос №1: Самый простой случай:

@implementation Person
@synthesize name;
@end

При этой настройке я предполагаю, что name будет автоматически выпущен, когда будет выпущен объект Person. На мой взгляд, компилятор просто вставляет [name release] в метод dealloc, как если бы я сам его набрал. Это правильно?


Вопрос № 2: Если я хочу написать свой собственный метод dealloc для этого класса, и я опускаю вызов [name release], это будет утечка?

@implementation Person
@synthesize name;
- (void)dealloc { [super dealloc]; }
@end

Вопрос № 3: Если я захочу написать свой собственный метод dealloc для этого класса, и я включу вызов [name release], это приведет к двойному выпуску, так как @synthesize уже позаботился об этом для меня?

@implementation Person
@synthesize name;
- (void)dealloc { [name release]; [super dealloc]; }
@end

Вопрос № 4: Если я захочу написать свой собственный аксессуар свойств для этого класса, но я не пишу свой собственный метод dealloc, будет ли утечка name?

@implementation Person
@dynamic name;
- (void)setName:(NSString *)newName
{
    [newName retain];
    [name release];
    name = newName;
}
@end

Вопрос № 5: У меня есть чувство (основанное на опыте), что ни один из вышеперечисленных сценариев не приведет к утечкам или двойным выпускам, поскольку язык был разработан, чтобы избежать их. Это, конечно, поднимает вопрос "как?". Является ли компилятор достаточно умным, чтобы отслеживать все возможные случаи? Что делать, если я должен был сделать следующее (обратите внимание, что это нелепый пример, просто предназначенный для иллюстрации моей точки):

void Cleanup(id object) { [object release]; }

@implementation Person
@synthesize name;
- (void)dealloc { Cleanup(name); }
@end

Неужели это приведет к тому, что компилятор добавит еще один метод [name release] к методу dealloc?

Ответ 1

Q1:

Нет. @synthesize не изменяет -dealloc для вас. Вы должны -release name самостоятельно.

Q2:

Да, он просочится. По той же причине, что и Q1.

Q3:

Нет, он не будет дважды выпущен. По той же причине, что и Q1.

Q4:

Да, он просочится. По той же причине, что и Q1.

Q5:

Нет, он не будет дважды выпущен. По той же причине, что и Q1.


Вы можете проверить это самостоятельно, переопределив -retain и -release и -dealloc, чтобы сообщить, что происходит.

#import <Foundation/Foundation.h>

@interface X : NSObject {}
@end
@implementation X
-(oneway void)release {
        NSLog(@"Releasing %p, next count = %d", self, [self retainCount]-1);
        [super release];
}
-(id)retain {
        NSLog(@"Retaining %p, next count = %d", self, [self retainCount]+1);
        return [super retain];
}
-(void)dealloc {
        NSLog(@"Dealloc %p", self);
        [super dealloc];
}
@end

@interface Y : NSObject {
        X* x;
}
@property (nonatomic, retain) X* x;
@end
@implementation Y
@synthesize x;
- (void)dealloc { [x release]; [super dealloc]; }
@end

int main () {
        NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
        Y* y = [[Y alloc] init];
        X* x = [[X alloc] init];
        y.x = x;
        [y release];
        [x release];
        [pool drain];                                                    
        return 0;
}

В Q1, Q2 и Q4 последний -retainCount of x равен 1, поэтому происходит утечка, а в Q3 и Q5 последний -retainCount равен 0 и -dealloc, поэтому существует нет утечки.

Ответ 2

Из Objective-C документация по свойствам:

dealloc

Объявленные свойства в корне место доступа декларации; когда вы синтезируете свойство, компилятор только создает любые отсутствующие методы доступа. Там есть нет прямого взаимодействия с dealloc свойства метода не автоматически выпущенный для вас. Объявленные свойства, однако, обеспечить полезный способ перекрестной проверки реализация вашего dealloc метод: вы можете искать все объявления свойств в заголовке файл и убедитесь, что объект свойства, не отмеченные выпущены, а отмеченные не выпущен.

Это, по сути, отвечает на все ваши вопросы.

Ответ 3

Простое и общее правило: если вы выделяете, сохраняете или копируете объект, вы должны его освободить.

Когда вы используете семантический параметр retain setter в инструкции @synthesize, вы просите компилятор построить для вас сеттер, который вызывает retain на объекте. Ни больше ни меньше. И поскольку вы сохраняете этот объект (даже если он с помощью волшебного автоматически сгенерированного кода), вы должны его выпустить, а где его выпустить - в -(void)dealloc.

Ответ 4

Что-то еще, что стоит знать - если у вас есть синтезированное свойство, установка этого свойства в nil (с использованием точечного синтаксиса, конечно) выдает вам ivar.