Почему я должен вызывать super -dealloc последним, а не первым?
Правильный пример:
- (void)dealloc {
[viewController release];
[window release];
[super dealloc];
}
неправильный пример:
- (void)dealloc {
[super dealloc];
[viewController release];
[window release];
}
Хотя почти во всех других случаях при переопределении метода я бы сначала вызвал реализацию метода super, в этом случае apple всегда в конце вызывает [super dealloc]. Зачем?
6 ответов
Это просто руководство. Вы можете позвонить другим инструкциям после [super dealloc]
, однако вы больше не можете получить доступ к переменным суперкласса, потому что они освобождаются при вызове [super dealloc]
, Всегда безопасно вызывать суперкласс в последней строке.
Также KVO и зависимые (запускаемые) ключи могут вызывать побочные эффекты, если они зависят от уже освобожденных переменных-членов.
Я ничего не знаю о программировании для iPhone, но я предполагаю, что по той же причине деструкторы должны вызываться в обратном порядке. Вы хотите убедиться, что весь ваш "мусор" вычищен перед вызовом вашего суперкласса. Если вы сделаете это с другой стороны, все может стать грязным. Например, если вашему деструктору необходим доступ к памяти, которую супердеструктор уже освободил:
class X {
private Map foo;
function __construct() {
foo = new Map();
}
function __destruct() {
foo.free;
}
}
class Y extends X {
function __construct() {
super.__construct();
map.put("foo", 42);
}
function __destruct() {
super.__destruct();
if (map.containsKey("foo")) { // boooooooooom!
doSomething();
}
}
}
Возможно, вы не столкнетесь с этой проблемой в своем коде, потому что "вы знаете, что делаете", но в целом безопаснее не делать такие вещи.
[super dealloc] освобождает память, используемую вашим объектом, включая указатели на viewController и window. Ссылка на переменные после их освобождения в лучшем случае опасна.
Смотрите этот ответ.
Вот фактический пример, где [super dealloc] должен быть последним, в противном случае вызов removeFromRunLoop вызовет сбой. Я не уверен, что происходит внутри removeFromRunLoop из NSOutputStream, но, похоже, в этом случае он обращается к 'self'.
Настроить:
[outputStream setDelegate:self];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
Dealloc:
- (void)dealloc {
if (outputStream) {
[outputStream close];
[outputStream removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[outputStream release];
outputStream = nil;
}
delegate = nil;
[super dealloc]; // must be last!
}
[к последнему сообщению] Не будет ли tableView, ссылающийся на делегата, отвечать за освобождение своего собственного делегата? Я думаю, что он сохранился, когда установлен (чтобы вы могли выпустить или автоматически выпустить), и он позаботится о себе?
Что касается вопроса OP, я всегда буду вызывать super сначала, если я создаю, и call super last, если я разрушаю. Я думаю об этом как "я хочу, чтобы супер построил то, что он хочет, чтобы я мог опираться на это, и я хочу, чтобы супер разрушали в последнюю очередь после того, как я уберу за собой". Хотя фактически все вызовы, которые я использую, создаются, кроме dealloc, поэтому вы всегда будете видеть это в моем коде dealloc.
У вас практически нет [super dealloc]
в конце, потому что это освобождает переменные суперкласса, и к ним больше нельзя получить доступ.
Единственное исключение - если у вас есть подкласс UITableViewController, который использует другой класс в качестве своего делегата табличного представления. В этом случае вы должны освободить делегат табличного представления после [super dealloc]
потому что табличное представление ссылается на делегат табличного представления, и табличное представление должно быть сначала освобождено.