Excel VBA質問箱 IV

当質問箱は、有志のボランティア精神のおかげで成り立っています。
問題が解決したら、必ずお礼をしましょうね。
本サイトの基本方針をまとめました。こちら をご一読ください。

投稿種別の選択が必要です。ご注意ください。
迷惑投稿防止のため、URLの入力を制限しています。ご了承ください。


7572 / 13646 ツリー ←次へ | 前へ→

【38194】クラスの解放を確認したい ハチ 06/5/29(月) 14:44 質問[未読]
【38202】Re:クラスの解放を確認したい 角田 06/5/29(月) 17:39 回答[未読]
【38207】Re:クラスの解放を確認したい ichinose 06/5/29(月) 18:08 発言[未読]
【38210】Re:クラスの解放を確認したい ハチ 06/5/29(月) 19:13 お礼[未読]
【38209】Re:クラスの解放を確認したい ハチ 06/5/29(月) 19:06 お礼[未読]
【38211】Re:そういうのは、もう作ってます 角田 06/5/29(月) 19:17 回答[未読]
【38213】Re:そういうのは、もう作ってます ハチ 06/5/29(月) 19:29 お礼[未読]
【38208】Re:クラスの解放を確認したい neptune 06/5/29(月) 18:46 回答[未読]
【38212】Re:クラスの解放を確認したい ハチ 06/5/29(月) 19:19 お礼[未読]

【38194】クラスの解放を確認したい
質問  ハチ  - 06/5/29(月) 14:44 -

引用なし
パスワード
   こちらのモーグ様のサイトを参照しながら、
クラスモジュールの使い方を勉強しています。
http://www.moug.net/skillup/opm/opm08-01.htm

自作のクラスからサブクラス(言い方あってます?)を
作成するように作ったのですが、動作は正常にできました。
終了するときに本当にクラスモジュールが解放されているか
確認する方法があれば教えてください。

Cont_Collクラスが、Coll_Eventを作成
Cont_Collが開放されるときに、作成されたColl_Eventも開放したいです。

UserForm1にCommandButtonを3つ、Labelを1つ作成

UserForm1モジュール
'---------
Private MyColl As New Cont_Coll


Private Sub Label1_Click()

MyColl.Con_Item(1).Caption = "テスト"

End Sub


Private Sub UserForm_Initialize()

Dim obj As MSForms.Control

For Each obj In Me.Controls
  If TypeName(obj) = "CommandButton" Then
    MyColl.Con_Add = obj
  End If
Next obj

End Sub

Private Sub UserForm_Terminate()
  Set MyColl = Nothing
End Sub
'------------

クラスモジュール"Cont_Coll"
'-----------
'Class名 Cont_Coll
'Control Collectionの意

Private m_Coll As New Collection 'Cont_Coll内部のコレクション
Private m_Item(3) As New Coll_Event 'サブクラスの要素数も指定

'コレクションへ追加するプロパティ
Public Property Let Con_Add(ByVal NewCont As MSForms.Control)
   m_Coll.Add Item:=NewCont
   Set m_Item(m_Coll.Count) = New Coll_Event
   m_Item(m_Coll.Count).ContItem = NewCont
End Property

'コレクションをカウントするプロパティ
Public Property Get Con_Count()
   Con_Count = m_Coll.Count
End Property

'コレクションItemを参照するプロパティ
Public Property Get Con_Item(ByRef m_Index As Integer)
   Set Con_Item = m_Coll(m_Index)
End Property

'Cont_Collが終了するときにColl_Eventで定義したm_Itemを全て開放したい
'ここが自信ありません。
Private Sub Class_Terminate()
Dim i As Integer
  For i = 1 To m_Coll.Count
    Set m_Item(i) = Nothing
  Next i
End Sub
'----------


クラスモジュール"Coll_Event"
'----------
'Class名 Coll_Event
'Collction Eventの意

Private WithEvents Cont_obj As MSForms.CommandButton

Public Property Let ContItem(ByVal NewCont As MSForms.CommandButton)
   Set Cont_obj = NewCont
End Property

'ここからEvent処理を書く

Private Sub Cont_obj_Click()
  MsgBox "クラスモジュールのイベント" & vbCr & Cont_obj.Caption
End Sub
'-------------

【38202】Re:クラスの解放を確認したい
回答  角田 WEB  - 06/5/29(月) 17:39 -

引用なし
パスワード
   こんにちは。
> Private MyColl As New Cont_Coll
こういう宣言時に New キーワードを付けると、アクセスする事そのものが
オブジェクトの生成に繋がりますから止めた方が良いでしょうね。
例えば、MyCollが既に解放されていたとしても
 If MyColl Is Nothing Then
と比較する行為そのものによって、再びMyCollにオブジェクトが割り当て
られてしまいます。

宣言時には New を付けずに
 Private MyColl As Cont_Coll
として、最初に使う直前で
 Set MyColl = New Cont_Coll
としてオブジェクトを生成するようにします。

そうすれば、Set MyColl = Nothing とした後は、上記のように
Newで生成しない限りは解放されたままです。

【38207】Re:クラスの解放を確認したい
発言  ichinose  - 06/5/29(月) 18:08 -

引用なし
パスワード
   こんにちは。
VBAをどこまで信用するかという事のにも関わってきますが。

Help等を見る限り、オブジェクトを参照している変数がなくなれば
オブジェクトに費やしたメモリ・リソースは開放されるとあります。

例えば、クラスモジュールClass1に
'=====================
Const mcnt = 20000
Dim myarray(1 To mcnt)
'=============================
Private Sub Class_Initialize()
  For i = 1 To mcnt
    myarray(i) = i
    Next
   
End Sub
'=============================
Private Sub Class_Terminate()
  Erase myarray()
  Debug.Print "erase"
End Sub


標準モジュールに

'===============================================
Dim mycls1 As Class1
Dim mycls2 As Class1
Dim mycls3 As Class1
'================================================
Sub test()
  Set myclass1 = New Class1
  Set myclass2 = myclass1
  Set myclass3 = myclass1
  Set myclass1 = Nothing
  Debug.Print "1"
  Set myclass2 = Nothing
  Debug.Print "2"
  Set myclass3 = Nothing
End Sub


このtestを実行すれば、イミディエイトには、
"erase"は一度だけ表示されていますよね?
(myclass3にNothingがセットされたときです)
このときにClass1というオブジェクトも開放されたと考えると・・・。


クラス'Cont_Collで

>'Cont_Collが終了するときにColl_Eventで定義したm_Itemを全て開放したい
>'ここが自信ありません。
>Private Sub Class_Terminate()
>Dim i As Integer
>  For i = 1 To m_Coll.Count
     m_item(i),id=i
>    Set m_Item(i) = Nothing
  Next i
End Sub


等としておいて、
クラスColl_Eventでは、


>クラスモジュール"Coll_Event"
>'----------
>'Class名 Coll_Event
>'Collction Eventの意

>Private WithEvents Cont_obj As MSForms.CommandButton
public id as long
>Public Property Let ContItem(ByVal NewCont As MSForms.CommandButton)
>   Set Cont_obj = NewCont
>End Property

>'ここからEvent処理を書く

>Private Sub Cont_obj_Click()
>  MsgBox "クラスモジュールのイベント" & vbCr & Cont_obj.Caption
>End Sub
Private Sub Class_Terminate()
  Debug.Print id
End Sub


などとしておいて正しくTerminateイベントが発生しているか確認してみる

という方法です。

コレクション以外で参照している変数があるとTerminateイベントは
発生しないでしょう?

検討してみてください。

【38208】Re:クラスの解放を確認したい
回答  neptune  - 06/5/29(月) 18:46 -

引用なし
パスワード
   ▼ハチ さん:
こんにちは
開放については先にResがついてますので省略。
要するに宣言は
>Private MyColl As New Cont_Coll

Private MyColl As Cont_Coll

にして、明示的にインスタンス作成、開放をしてやるのが確実です。


>自作のクラスからサブクラス(言い方あってます?)
間違ってます。なんていうのかな?あえて言うなら子クラスですかね。
正規の呼び方があるかどうはかは知りません。

サブクラスは別の特定の意味があって、Windowのメッセージの代行処理、
の意味が一般的なように思います。

【38209】Re:クラスの解放を確認したい
お礼  ハチ  - 06/5/29(月) 19:06 -

引用なし
パスワード
   ▼角田 さん:

Resありがとうございます。

動作させる度に、なんだかExcelが重くなっているような気がして
連携して作成されたオブジェクトが削除されていないのではないか? と考え
今回の質問に至りました。

角田さんのご指摘のようにMyCollを何度も生成していたようでした。
Classの用語の「インスタンス」と「オブジェクト」がイマイチ理解できておらず、
Private MyColl As New Cont_Coll でオブジェクトを作成し、
Set でインスタンスを格納しているのだ と勝手に理解していましたので
もっと勉強します。

いろいろなサイトで調べていたところ、
「VBAではコントロール配列を処理することができない」との記載があり、
それならクラスでCollectionの配列にして処理すれば、
本体側のモジュールがすっきりするのではないか?
とのコンセプトで作っていました。
もっと改良が必要そうです・・・

【38210】Re:クラスの解放を確認したい
お礼  ハチ  - 06/5/29(月) 19:13 -

引用なし
パスワード
   ▼ichinose さん:

Resありがとうございます。
提示して頂いたコードでClass_Terminate()が実行されていることを
確認することができました。
ありがとうございました。

今回はMsgBoxで確認するコードをClass_Terminateにコメントアウトして追記しました。

'-------
'Class名 Cont_Coll
'Control Collectionの意

Private m_Coll As Collection 'Cont_Coll内部のコレクション
Private m_Item(3) As Coll_Event '子クラスの要素数を指定

'クラスが作成されたときにCollection作成
Private Sub Class_Initialize()
  Set m_Coll = New Collection
End Sub

'コレクションへ追加するプロパティ
Public Property Let Con_Add(ByVal NewCont As MSForms.Control)
   m_Coll.Add Item:=NewCont
   Set m_Item(m_Coll.Count) = New Coll_Event
   m_Item(m_Coll.Count).ContItem = NewCont
End Property

'コレクションをカウントするプロパティ
Public Property Get Con_Count()
   Con_Count = m_Coll.Count
End Property

'コレクションItemを参照するプロパティ
Public Property Get Con_Item(ByRef m_Index As Integer)
   Set Con_Item = m_Coll(m_Index)
End Property


Private Sub Class_Terminate()
Dim i As Integer
  For i = 1 To m_Coll.Count
    Set m_Item(i) = Nothing
  Next i
  
  'MsgBox "Cont_Coll開放" '確認用
End Sub

'-------
'Class名 Coll_Event
'Collction Eventの意

Private WithEvents Cont_obj As MSForms.CommandButton

Public Property Let ContItem(ByVal NewCont As MSForms.CommandButton)
   Set Cont_obj = NewCont
End Property

Private Sub Class_Terminate()
  'MsgBox Cont_obj.Caption & "のColl_Event開放" '確認用
End Sub

'ここからEvent処理を書く

Private Sub Cont_obj_Click()
  MsgBox "クラスモジュールのイベント" & vbCr & Cont_obj.Caption
End Sub

【38211】Re:そういうのは、もう作ってます
回答  角田 WEB  - 06/5/29(月) 19:17 -

引用なし
パスワード
   こんにちは。
>「VBAではコントロール配列を処理することができない」との記載があり、
>それならクラスでCollectionの配列にして処理すれば、
「擬似からの脱却」
http://www.h3.dion.ne.jp/~sakatsu/Breakthrough_P-Ctrl_Arrays.htm

【38212】Re:クラスの解放を確認したい
お礼  ハチ  - 06/5/29(月) 19:19 -

引用なし
パスワード
   ▼neptune さん:

Resありがとうございます。

>>自作のクラスからサブクラス(言い方あってます?)
>間違ってます。なんていうのかな?あえて言うなら子クラスですかね。
>正規の呼び方があるかどうはかは知りません。
>
>サブクラスは別の特定の意味があって、Windowのメッセージの代行処理、
>の意味が一般的なように思います。

どこかのサイトに上位のクラスを「親クラス」parentと書いてあった記憶があります。
「子クラス」で検索すると大量にひっかかりますので
「子クラス」か「派生クラス」で今後は表記するようにします。
ありがとうございました。

【38213】Re:そういうのは、もう作ってます
お礼  ハチ  - 06/5/29(月) 19:29 -

引用なし
パスワード
   ▼角田 さん:
>こんにちは。
>>「VBAではコントロール配列を処理することができない」との記載があり、
>>それならクラスでCollectionの配列にして処理すれば、
>「擬似からの脱却」
>http://www.h3.dion.ne.jp/~sakatsu/Breakthrough_P-Ctrl_Arrays.htm

アドインを作成されていたのですね!
詳しく拝見させて頂きます。

ありがとうございました。

7572 / 13646 ツリー ←次へ | 前へ→
ページ:  ┃  記事番号:
2610219
(SS)C-BOARD v3.8 is Free