Каким образом релиз обрабатывается для сохраняемых свойств @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.