Я просматривал код для создания слабых ссылок в VBA путем ручного разыменования указателей объектов без вызова IUnknown::AddRef
, и обнаружил ошибку, которую не могу объяснить. Я мог бы придумать минимально воспроизводимый пример с использованием чистых вызовов API, но я думаю, что проще просто продемонстрировать использование класса WeakReference
из этого обзора. Вот как я могу sh Excel:
Dim strongRef As Range
Set strongRef = [A1]
Dim weakRef As New WeakReference
weakRef.Object = strongRef
Debug.Assert strongRef.address = weakRef.Object.address 'fine
Set strongRef = Nothing
Debug.Assert weakRef.Object Is Nothing 'fine if step through, crashes if F5
Это ошибочное поведение; класс WeakReference
разработан таким образом, что после уничтожения родительской ссылки слабая ссылка должна возвращать Nothing
, а не пытаться вслепую разыменовать родительский ObjPtr
, который теперь указывает на недопустимый экземпляр объекта. То, как он это делает, подробно объясняется в связанном вопросе, но по сути он кэширует указатель VTable родительского объекта, а затем использует его для проверки того, что указатель VTable все еще действителен перед каждым разыменованием. В основном класс полагается на тот факт, что, когда родительский объект выходит за пределы области видимости, его память освобождается, и поэтому указатель VTable перезаписывается чем-то другим.
Этот должен остановить этот вид ошибка. Однако это не так, и мне интересно, почему ...
Насколько я понимаю,
Set strongRef = Nothing
- Вызовы
IUnknown::Release
- Это устанавливает счетчик ссылок на ноль, объект выходит за пределы области видимости
- Объект отвечает за освобождение своей собственной памяти экземпляра, поэтому он использует указатель
this
(первый аргумент на IUnknown::Release
), чтобы обнулить память экземпляра (включая указатель VTable) и освободить ее для использования снова распределитель памяти VBA - Наконец, значение в
VarPtr(strongRef)
устанавливается в ноль, чтобы указать, что это ссылка на нулевой объект
Однако я думаю, что ошибка возникает из-за того, что память экземпляра не сбрасывается, как только счетчик ссылок достигает нуля, поэтому, возможно, реализация VBA IUnknown::Release
помечает память как «грязную», которая должна быть очищена позже асинхронным сборщиком мусора? Я здесь просто догадываюсь. Дело в том, что если я прохожу код построчно, он работает нормально, или если вы держите WeakReference
в дочернем классе, тогда он работает нормально (см. Примеры в связанной публикации).
ОБНОВЛЕНИЕ
Я только что попробовал, с настраиваемым классом VBA для strongRef
, например
Class1
Option Explicit
Static Property Get address() As Double
Dim value As Double
If value = 0 Then value = [RandBetween(1,1e10)]
address = value
End Property
... тогда я не получаю cra sh! Так что это определенно как-то связано с конкретными c реализациями IUnknown::Release
, и, вероятно, именно поэтому автор этого кода никогда не замечал ошибки.