Прочитал на днях пост GunSmoker-a о том, что всегда нужно использовать FreeAndNil вместо Free. Прочитал, подивился, взял на заметку попробовать завести такую привычку, хотя так и не смог придумать объяснения, кроме как: а почему бы и нет?
И вот бывает же! Как раз сегодня наткнулся на ситуацию, когда из деструктора объекта происходило обращение к самому объекту по имени переменной. Конечно обращение происходило не из самого объекта, а из одного из вложенных. Кстати, дело было в коде Lazy Delphi Builder.=) Из-за частых изменений в иерархии классов, и постоянной работе над проектом в режиме “скорей бы уже релиз выпустить” у меня там развёлся довольно милый бардачок, с большим количеством глобальных переменных.
Итак..
Итак, есть у меня глобальный объект LazyInstaller, который в конструкторе создаёт другой глобальный объект – главную форму. Форма довольно активно использует LazyInstaller, а LazyInstaller изредка обращается к форме. Так вот следуя правилу “кто объект создаёт, тот его и разрушает”, у LazyInstaller-а в деструкторе прописано уничтожение главной формы. Форма в свою очередь уничтожает VirtualTreeView, а VirtualTreeView, при уничтожении вызывал событие OnFreeNode, обработчик которого, в свою очередь обращался к LazyInstaller-у, который в тёмном чулане хранится в доме, который построил Джек.
До тех пор пока LazyInstaller освобождался так
LazyInstaller.Free;
LazyInstaller:=nil;
всё работало нормально. Вызываемый метод просто возвращал nil, который проверялся в обработчике события и всё вроде работало нормально.
В рамках очередного рефакторинга, я заменил тип переменной с объектной на интерфейсную. Соответственно изменилось и уничтожение. Если раньше было:
var
LazyInstaller : TLazyInstaller;
...
LazyInstaller .Free;
LazyInstaller :=nil;
А после замены на интерфейс стало:
var
LazyInstaller : ILazyInstaller;
...
LazyInstaller :=nil;
Случилось то же самое, что и при использовании FreeAndNil. Сначала переменная LazyInstaller становилась равной nil, и уже после этого вызывался деструктор объекта.
Так что, при выходе из программы стал появляться загадочный AV. Замучался его выслеживать. Только после того, как понаставил повсюду assert(assigned(LazyInstaller)) смог определить это место. Ещё одна такая ошибка и начну заменять глобальные переменные на функции, чтобы проверять assert(assigned(LazyInstaller)) в одном месте, а не рыскать по всему коду. =)
Версия для мобильного

6 человек заметили этот пост: