Подтвердить что ты не робот

Счетчик ссылок VBA - уничтожение объекта

В последнее время я столкнулся с вопросом, который заставил меня задуматься; это делало меня занятым, и я не мог найти прозрачного объяснения этому в сети.
Это связано с уничтожением объектов Excel (которые я использую все время и никогда не подвергался допросу раньше).

Предыстория, ведущая к моему вопросу:
С помощью обычных объектов вы можете создать объект, используя ключевые слова SET и NEW. Например:

Set classInstance = New className

Всякий раз, когда мы создаем этот способ, объект создается в памяти кучи, а счетчик ссылок увеличивается на 1.
В случае, если я не добавлю больше ссылок, следующий оператор вернет счетчик ссылок к нулю:

Set classInstance = Nothing 

Когда счетчик ссылок переходит в 0, объект уничтожается и очищается от памяти, а точка "classInstance" указывает на.

Что я читал:
Когда мы используем функцию CREATEOBJECT, она возвращает ссылку на объект COM.

Set oApp = CreateObject("Excel.Application")

Даже если бы мы могли сказать:

Set oApp = nothing 

Счетчик ссылок объектов будет равен 0, а oApp больше не будет указывать на объект.

Мои вопросы:
1) Почему этот тип объекта требует вызова метода .Quit перед тем, как объект фактически удаляется из памяти?

То же самое происходит при добавлении ссылки на объект рабочей книги (workbooks.add или workbook.open), который требует метода .close. Почему эти объекты не могут быть автоматически уничтожены при приведении счетчика ссылок в ноль?
Например, когда мы говорим:

set oRange = nothing 

2) И нужно ли говорить:

oApp.Quit
set oApp = nothing 

Так как объект приложения уже очищен от памяти при применении .Quit, объект больше не будет выпущен.
Единственная причина, по которой я мог бы придумать, почему oApp будет настроен на Nothing после Quit, будет потому, что он может указывать на неиспользуемую ячейку памяти (в куче) и позже может привести к путанице, если эта память будет повторно назначена (хотя в VBA мне это трудно представить). Я спрашивал себя, правильно ли это заключение, и я хотел бы получить подтверждение от этого, кто знает ответ.
Пожалуйста, скажите мне, если я вижу это неправильно.

3) То, что они называют в VBA "ссылкой на объект" (например, oApp в коде выше), я вижу их как переменные указателя в C. Было бы безопасно использовать это утверждение или снова, я вижу это неправильно?

Как правило, не сложно применить .Quit и установить ничего, но было бы неплохо получить точную информацию по этой теме. Так что я знаю на 100%, почему я это делаю.

4b9b3361

Ответ 1

Хороший вопрос:)

Excel управляет созданием своих объектов. Подобным образом он также контролирует их уничтожение.

Настройка oApp = Nothing просто уничтожает ссылку на объект. Он не удаляет приложение. Чтобы уничтожить объект Excel, вы должны использовать метод .Quit.

Всякий раз, когда вы делаете, Set x = Nothing, ссылка (указатель) с именем x на соответствующий объект удаляется. Это не означает, что сам объект будет удален из памяти. Будет ли объект удален из памяти или нет, зависит от различных факторов.

  • Есть ли больше ссылок, указывающих на один и тот же объект. Если есть, объект не будет удален. Счетчик ссылок должен быть равен нулю.
  • Внутренняя реализация деструктора этого объекта.

Метод .Quit определен для того, чтобы любезно удалить все объекты памяти, которые выделил excel, и закрыть себя.

Он похож на вызов Close в форме в VB6. Возьмем, к примеру, форму в vb6.

Dim f As Form
Set f = Form1
f.Show

'
'~~> Rest of the code
'

Set f = Nothing

Это разрушит форму?:)

Followup

Как насчет вопроса 2? Спасибо - Kim Gysen 14 мин назад

enter image description here

Это может быть не так, как показано здесь, а оптимизация компилятора может привести к тому, что все будет вести себя по-другому... но это основная концепция, которая работает.

Ответ 2

Часть 2 вашего вопроса довольно интересна, и это стоит большой ответ.

Это будет охватывать три ключевых момента:

  • Объекты и переменные объекта;
  • Ловушки при удалении объектов;
  • ... И важное изменение в reference-counting объект приложения в Excel 2013.
Но, если вы хотите получить короткий ответ, это: "Не все объекты равны".

Теперь читайте дальше...

Некоторые объекты создаются в вашем собственном "пространстве" сеанса Excel, а их распределение памяти контролируется вашей сессией; некоторые объекты имеют постоянные компоненты, которые существуют после отклонения объектной переменной; а некоторые нет:


Set oDict = CreateObject("Scripting.Dictionary")
Set oWShell = CreateObject("Shell.Application")

В обоих случаях выделена память, переменные объекта (и их vTable указателей на методы и свойства) создаются, и они должны командовать до тех пор, пока вы не отклоните их:


Set oDict = Nothing
Set oWShell = Nothing

И, при увольнении, их не осталось.

Но этот объект является постоянным:


Dim oWbk as Excel.Workbook
Set oWbk = Application.Workbooks.Add
... Вы создали новый объект книги и, если вы отклоните объектную переменную с помощью Set oWbk = Nothing, вы увидите, что новый объект книги по-прежнему существует как видимое присутствие в пользовательском интерфейсе.

То, что вы на самом деле создали, - это Workbook object - окно рабочей книги с активным листом и полным пользовательским интерфейсом, который идет с этим, - и переменная объекта рабочей книги - a программный COM-интерфейс, таблица методов и свойств объекта Workbook - вы можете манипулировать в коде с использованием именованного объекта "oWbk".

Отказ от объектной переменной oWbk удаляет эту структуру, но сама книга все еще существует: вы создали объект Workbook и сохранили его.

Объект больше, чем его объектная переменная, и отклонение переменной не уничтожает объект: он просто отклоняет интерфейс, структуру методов и свойств, которые можно использовать для управления объектом в коде.

Закрытие рабочей книги с сохранением файла или без него должно автоматически отклонять объектную переменную и очищать память, выделенную для этого интерфейса свойств, методов и атрибутов:


'try this:
oWbk.Close SaveChanges:=False
' or maybe this:
Application.Workbooks(Application.Workbooks.Count).Close SaveChanges:=False 
... То есть вы ожидали бы, что обе эти команды вызовут Set oWbk= Nothing - особенно команду oWbk.Close, но если вы попробуете любой из них без явного отклонения oWbk, вы обнаружите, что oWbk все еще существует как пустая шелуха, и все вызовы и запросы для получения информации об этом (try > Debug.Print> TypeName(oWbk)  ) вернут сообщения "Ошибка автоматизации" .

Некоторые из предложений в предыдущем ответе упоминают объект UserForm, который, в отличие от словаря и объекта Shell, - это объект с видимым пользовательским интерфейсом. Но этот пользовательский интерфейс не является постоянным новым объектом в пользовательском интерфейсе Excel, например, рабочей книжке или листе.

К счастью для вас, созданный вами объект принадлежит вашему сеансу Excel, и вы можете снова создать объектную переменную, чтобы получить ту же структуру методов и свойств и снова взять под свой контроль объект:


Set oWbk = Application.Workbooks(Application.Workbooks.Count)
... Предполагая, конечно, что у вас есть некоторый способ убедиться, что вы определили правильный объект рабочей книги: но это не ваш вопрос вообще.

В этом случае этот ответ: объекты, которые не созданы в вашей собственной памяти Excel.


Set oApp = CreateObject("Excel.Application")
Этот оператор создаст объект Excel, который, как и новая рабочая книга, имеет пользовательский интерфейс (хотя вам нужно будет установить свойство .Visible True, чтобы увидеть его), и постоянное присутствие в памяти: еще раз объект больше, чем его объектная переменная, и устранение этой переменной не разрушает объект.

В отличие от новой рабочей книги, вам не совсем нужно: это сам сеанс Excel, он выделяет собственную память - oApp "footprint" в вашей текущей памяти сеанса - это только указатель и имя: интерфейс (vTable, iDispatch и все эти именованные методы с указателями на структуры, реализующие тайный акт манипулирования сеансом Excel в VBA) существует в блоке памяти, выделенном этим новым сеансом Excel.

Вот что происходит в Office 2010 и более старых версиях Excel:

Отключение объектной переменной с помощью Set oApp = Nothing приведет к тому, что сеанс запущен и работает, и я настоятельно рекомендую вам сделать сеанс видимым, чтобы вы могли закрыть его вручную!

Закрытие сеанса Excel вручную без явного отклонения переменной объекта oApp будет определеннооставить oApp в состоянии "пустой шелухи", а мрачный и безголовый призрак "Объект автоматизации отключен от своих клиентов!" в темных углах вашей базы кода.

Но в Office 2013 и более поздних версиях Set oApp = Nothing выполняет точно подсчет ссылок, который вы ожидаете, и сеанс закрывается. Попробуй:


Private Sub Test()
Dim oApp As Excel.Application
Set oApp = New Excel.Application 'Set oApp = CreateObject("Excel.Application")
oApp.Visible = True
Set oApp = Nothing
End Sub
Он не будет закрываться на Set oApp = Nothing, если другая объектная переменная имеет ссылку - и это не единственный объект, который получает для увеличения счетчика ссылок: активность пользователя в графическом интерфейсе (попытка создания новой рабочей книги и ее редактирования) удерживает сеанс вверх и работает тоже.

Для вашего собственного развлечения посмотрите, действительно ли oApp.Quit отклоняет oApp и устанавливает его на Nothing.

Конечно, oApp.Quit обязательно закроет сеанс...

... Или это будет? Если в этом сеансе что-то происходит - длинный расчет или сообщение об ошибке "модальное", которое вы должны увидеть и щелкнуть, прежде чем объект приложения ответит на любой другой вход, из пользовательского интерфейса или ваш VBA - тогда oApp.Quit не закроет сеанс.

Не нужно туда идти. При прочих равных условиях oApp.Quit обязательно закроет сеанс в 2010 году и более ранних версиях Excel.

Но в Office 2013 вызов "Выход" из последней переменной объекта просто скрывает пользовательский интерфейс: переменная объекта по-прежнему отвечает на ваш код - методы и свойства, которые не требуют активной рабочей книги, по-прежнему доступны через oApp - и отдельный экземпляр Excel.exe отчетливо отображается на вкладке "Процессы" диспетчера задач.

Аналогично, выход из нового сеанса нажатием кнопки "закрыть" в пользовательском интерфейсе закрывает окна сеанса, но если в вашем коде есть объектная переменная со ссылкой на этот объект приложения, она все еще там, в памяти и 'oApp' все равно может получить свойства и методы.

Таким образом, счетчик ссылок работает в обоих вариантах в текущих версиях Excel: объект существует до тех пор, пока счетчик ссылок не уменьшится до нуля, а последняя оставшаяся переменная объекта будет не оставлена ​​ "отключена" путем завершения команды или пользовательского интерфейса.

Тем не менее, ваша сессия не "владеет" этим новым объектом приложения: если вы упустили последнюю переменную объекта и установили ее в Nothing, и там что-то еще поддерживало новый сеанс - активность пользователя или внутренний процесс - нет ничего похожего на сборку Application.Workbooks() или Worksheets() для идентификации других сеансов Excel и создания объектной переменной, указывающей на конкретный экземпляр объекта Excel.Application.

Существуют способы получения определенного сеанса с использованием вызовов API, но они не столь надежны, как вы могли бы пожелать.

... Итак, в целом, в этой "части 2" довольно много.