Совет номер один, который вы услышите при работе с интерфейсами, заключается в том, чтобы никогда не смешивать ссылки на интерфейсы с ссылками на объекты . Это означает, что как только вы начинаете ссылаться на объект через ссылку на интерфейс, вы перестаете ссылаться на него через ссылку на объект. Когда-либо.
Причина в том, что при первом назначении интерфейсной переменной счетчик ссылок объекта становится равным 1. Когда эта переменная выходит из области видимости или получает новое значение, счетчик ссылок становится равным нулю, и объект освобождается сам. Это все без какой-либо модификации исходной переменной объекта-ссылки, поэтому, когда вы позже попытаетесь использовать эту переменную, это не пустой указатель, а объект, на который она ссылается, ушел - это свисающая ссылка . Когда вы пытаетесь освободить то, что не существует, вы получаете исключение операции неверного указателя.
Объявите вашу переменную ConfigManager
как интерфейс. Не освобождай это сам. Как только вы это сделаете, вы можете переместить всю декларацию TConfigManager
в раздел реализации, потому что ни один код вне этого модуля никогда не будет ссылаться на него.
Кроме того, редко есть какая-либо причина предоставить собственную реализацию QueryInterface
. (Вы сказали, что отвергли это, но это невозможно, потому что это не виртуально.) Достаточно того, что предоставлено TInterfacedObject
. Тот, который вы предоставляете, на самом деле вызывает утечку памяти, потому что вы увеличиваете счетчик ссылок, когда этого не должно быть. GetInterface
уже вызывает _AddRef
(выполняя назначение интерфейса), поэтому вы возвращаете объекты с завышенным количеством ссылок.