Вопрос по перекрестным ссылкам

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Вопрос по перекрестным ссылкам

Сообщение alibek » 14.02.2006 (Вт) 11:36

Вот с таким вопросом застрял :)

Есть объект Document.
У него есть иерархическая коллекция Tags, в которой хранятся элементы Tag, каждый из которых может иметь свою дочернюю коллекцию Tags (в Document хранится иерархическая коллекция HTML-тэгов).

Вот пример заполнения документа:
Код: Выделить всё
Dim doc As New Document
With doc.Tags
  .Add "DOCTYPE", True
  With .Add("HTML").Childs
    With .Add("HEAD").Childs
      .Add "TITLE"
      .Add "BASE"
      .Add "META"
    End With
    With .Add("BODY").Childs
      .Add "P"
      With .Add("TABLE").Childs
        With .Add("TR").Childs
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
        End With
      End With
      .Add "IMG"
      With .Add("DIV").Childs
        .Add "IMG"
      End With
    End With
  End With
End With


Допустим, есть такой код:
Код: Выделить всё
Dim T As Tag
With tvwTest.Nodes
  .Clear
  For Each T In doc.Tags
    Nodes.Add Text:=T.Name
  Next T
End With
doc.CleanUp
Set doc = Nothing

В doc.CleanUp выполняется очистка данных (обнуляются ссылки и т.п.).
Так вот, после Set doc = Nothing в процедуре имеется одна незакрытая ссылка на Tags (если конкретно, то на doc.Tags). В doc.CleanUp эта ссылка обнуляется, значит есть еще одна объектная переменная, ссылающаяся на нее.

Если убрать For Each, то лишней ссылки не остается.

Если For Each оформить так:
Код: Выделить всё
Dim C As Tags
Set C = doc.Tags
For Each T In C
  ...
Next T
Set C = Nothing

то лишней ссылки также не остается.

Или если вынести For Each в отдельную процедуру
Код: Выделить всё
Private Sub test(doc As Document, Nodes As Nodes)
Dim T As Tag
For Each T In doc.Tags
  Nodes.Add Text:=T.Name
Next T
End Sub

и вместо For Each вызывать Call test, то лишней ссылки также не остается.

Больше всего похоже на то, что For Each создает временную переменную, но не очищает ее, когда цикл отрабатывает.


Вопрос простой: что делать? :)
Lasciate ogni speranza, voi ch'entrate.

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 14.02.2006 (Вт) 15:08

Впервые о таком слышу. Никогда раньше не сталкивался.

А в каком месте у тебя сейчас этот код, если его вынос в отдельный суб решает проблему?

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 14.02.2006 (Вт) 15:11

Я тоже, и мы не смогли найти решение в привате :)

Если перечислитель коллекции находится в той же процедуре, что и создание/уничтожение объекта, то объект не уничтожается - так, словно на момент вызова CleanUp существует та самая временная ссылка, созданная перечислителем, которая по идее должна отрелизиться в момент выхода из цикла.
Последний раз редактировалось GSerg 14.02.2006 (Вт) 15:15, всего редактировалось 1 раз.
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 14.02.2006 (Вт) 15:13

Сейчас код в основном месте :)
Просто хотелось бы данный класс сделать максимально автономным и удобным для использования. Перебор в отдельной процедуре или создание временной переменной для коллекции в крайнем случае тоже пойдет, но "на экспорт" так не делают :)
В принципе, даже CleanUp на "экспорт" не делается, но ничего более подходящего (кроме выноса во внешний ActiveX) не приходит в голову.
Lasciate ogni speranza, voi ch'entrate.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 14.02.2006 (Вт) 16:53

Скорее всего проблема в определении через As New Document. Цикл For Each нормально отрабатывает и не имеет побочных эффектов.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 14.02.2006 (Вт) 17:10

Имел тайное желание, чтобы появился Vi, желание исполнилось :)
Что может обуславливать такое поведение?
Код класса Document:
Код: Выделить всё
Option Explicit

Private varReadOnly As Boolean
Private varFlatMode As Boolean

Private objHTMLStore As TextStore
Private objTags As Tags

Public Property Get Tags() As Tags
Set Tags = objTags
End Property

Public Property Get HTMLStore() As TextStore
Set HTMLStore = objHTMLStore
End Property

Public Sub CleanUp()
Dispose
End Sub

Friend Sub Create()
Set objTags = New Tags
objTags.Create Nothing, Me, objHTMLStore
objTags.SetReadOnly varReadOnly
objTags.SetFlatMode varFlatMode
End Sub

Friend Sub Dispose()
If Not objTags Is Nothing Then
  objTags.Dispose
  Set objTags = Nothing
End If
End Sub

Private Sub Class_Initialize()
Print #1, "Document + :: " & ObjPtr(Me)
varReadOnly = True
varFlatMode = True
Set objHTMLStore = New TextStore
Create
End Sub

Private Sub Class_Terminate()
Print #1, "Document - :: " & ObjPtr(Me)
Set objHTMLStore = Nothing
End Sub


Вроде бы никакого криминала нет.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 14.02.2006 (Вт) 17:13

Такая инициализация:
Код: Выделить всё
Dim doc As Document
Set doc = New Document

поведение не меняет. Т.е. один инстанс Tags (doc.Tags) висит как и раньше, пока процедура не завершит работу.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 14.02.2006 (Вт) 17:17

Вот еще что выяснил.
Если For Each не используется, то объекты закрываются в таком порядке:
Tags (doc.Tags)
Document (doc)
TextStore (doc.TextStore)
Если цикл For Each используется, то порядок закрытия такой:
Document (doc)
TextStore (doc.TextStore)
<конец процедуры>
Tags (doc.Tags)
Lasciate ogni speranza, voi ch'entrate.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 15.02.2006 (Ср) 7:29

А не мог бы ты, если не секрет, выложить коды всех классов и точную проблемную процедуру? Если секрет, то можно в приватный мейл с гарантией неразглашения.
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.02.2006 (Ср) 8:44

Ну поскольку я все-равно собирался выложить исходники, когда проект будет закончен, то не секрет :)

Дополнительно необходим код:
Код: Выделить всё
Public Const vbSpace As String = " ", vbSpace2 As String = "  "
Public Const vbSkip As String = "-"
Public Const vbEmptyDate As Date = 0


Проблемная процедура:
Код: Выделить всё
Open "C:\debug.txt" For Output As #1
Dim doc As Document, T As Tag
Set doc = New Document
With doc.Tags
  .Add "DOCTYPE", True
  With .Add("HTML").Childs
    With .Add("HEAD").Childs
      .Add "TITLE"
      .Add "BASE"
      .Add "META"
    End With
    With .Add("BODY").Childs
      .Add "P"
      With .Add("TABLE").Childs
        With .Add("TR").Childs
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
        End With
        With .Add("TR").Childs
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
        End With
        With .Add("TR").Childs
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
          With .Add("TD").Childs
            .Add "P"
            .Add "P"
            .Add "P"
          End With
        End With
      End With
      .Add "IMG"
      With .Add("DIV").Childs
        .Add "IMG"
      End With
    End With
  End With
End With
For Each T In doc.Tags
  Debug.Print T.Name
Next T
doc.CleanUp
Set doc = Nothing
Print #1, "***"
'Close #1
Вложения
test.zip
Набор проблемных классов.
(9.04 Кб) Скачиваний: 23
Lasciate ogni speranza, voi ch'entrate.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 15.02.2006 (Ср) 12:50

Я тоже присоединяюсь к вышеприведенным соображениям и беру назад свои слова о том, что "Цикл For Each ... не имеет побочных эффектов". Как оказывается, имеет. Видимо, реализацию For Each писали студенты (индийские :)).

Причем вложенные For Each (например, For Each T In doc.Tags: For Each v In T.Childs ... ) приводят к удержанию последней коллекции во вложенном цикле и не увеличивают общее количество удерживаемых. Поэтому соответствие переменных и циклов 1:1. По-видимому, это последняя полезная информация.

***EDIT*** Это - задействование переменной - относится к сложным вычислениям объекта (например, "doc.Tags" и т.п.), у которого берут коллекцию. Видимо, были случаи, когда разрушение объекта приводило к разрушению коллекции. Поэтому и сохраняют. ***EDIT***

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



vbskb_bug vbskb_collection
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.02.2006 (Ср) 13:02

Сенкс :)
Ну что ж, буду кодить с учетом этого.
Lasciate ogni speranza, voi ch'entrate.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.02.2006 (Ср) 14:56

Ура! :)

В классе Document я добавил новую проперть:
Код: Выделить всё
Public Property Get NewEnum() As IUnknown
Set NewEnum = objTags.NewEnum
End Property


Определил эту проперть, как скрытую (Hidden) и перечислитель (DispID = -4).

С клиентской стороны теперь тэги перебираются так:
Код: Выделить всё
For Each T In doc
  Debug.Print T.Name
Next T

Все работает.


Сенкс tyomitch:)
Lasciate ogni speranza, voi ch'entrate.

Ennor
Конструктивный критик
Конструктивный критик
 
Сообщения: 2504
Зарегистрирован: 18.12.2001 (Вт) 3:58
Откуда: Калуга -> Москва

Сообщение Ennor » 15.02.2006 (Ср) 15:09

Ты этого не знал?Я идиот! Убейте меня, кто-нибудь!

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 15.02.2006 (Ср) 15:20

Не догадался :)
Lasciate ogni speranza, voi ch'entrate.

Vi
Постоялец
Постоялец
 
Сообщения: 739
Зарегистрирован: 25.01.2002 (Пт) 11:03
Откуда: Россия, Ижевск

Сообщение Vi » 15.02.2006 (Ср) 16:20

А попробуй эквивалентное "For Each T In doc.Tags.Document"? И потом как ты запретишь клиенту использовать более красивое и логичное "For Each T In doc.Tags"? И к чему это может привести (именно у клиента)?
Vita
Выше головы не прыгнешь, ниже земли не упадешь, дальше границы не убежишь! (с) КВН

tyomitch
Пользователь #1352
Пользователь #1352
Аватара пользователя
 
Сообщения: 12822
Зарегистрирован: 20.10.2002 (Вс) 17:02
Откуда: חיפה

Сообщение tyomitch » 15.02.2006 (Ср) 19:05

Vi писал(а):А попробуй эквивалентное "For Each T In doc.Tags.Document"? И потом как ты запретишь клиенту использовать более красивое и логичное "For Each T In doc.Tags"? И к чему это может привести (именно у клиента)?

По крайней мере, теперь сделать неправильно стало сложнее, чем сделать правильно ;-)
Изображение


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3

    TopList