[[20041124212113]] 『VBA:変数の開放の必要性』(みやほりん) >>BOT

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『VBA:変数の開放の必要性』(みやほりん)
 いつもお世話になっています。
 今日は「変数の開放」についての質問をさせていただきます。
[[20041124113752]] 『各シートごと選択されているセル範囲の格納』(kaze) 
こちらのトピックで[川野鮎太郎]さんが
>セットしたら開放
と書いておられます。
モジュールレベル変数やグローバル変数として宣言したものは
そのような開放処理の必要な場合があるのは理解できますが、
私はプロシージャレベルで宣言した変数は、そのプロシージャの実行が
終了した時点で自動的に開放されると理解しているものですから、
サンプルコードで最後に「Set 変数 = Nothing」なんて見ると
「あれっ?」と感じてしまうわけです。
 
【質問】
プロシージャレベルの変数でも最後に開放しておかなければいけないものでしょうか。
 
その理由や情報をご存知の方、お教えください。
お願いいたします。


 珍しい方のご質問ですね・・・。
 私の書いたもので疑問が出ているのに知らんふりは出来ませんね(^_^A;

 私は今までNothingは使ってなかったんですが、
 みやほりんさんの言われるように、プロシージャレベルで宣言した場合は必要ないですね。
 こんな感じでモジュールレベルで宣言した場合にのみ開放しないといけない(した方が良いかな?(^_^A;)みたいですね。
 こちらは必要で、
 Option Explicit
Dim MyRange1 As Range
Sub Test()
 Set MyRange1 = Worksheets("Sheet2").Range("A1:A50")
 Set MyRange2 = Worksheets("Sheet3").Range("A2")
 MyRange1.Copy Destination:=MyRange2
 Set MyRange1 = Nothing
 Set MyRange2 = Nothing
End Sub

 こちらは不要なんですね。
 Option Explicit
Sub Test()
Dim MyRange1 As Range
 Set MyRange1 = Worksheets("Sheet2").Range("A1:A50")
 Set MyRange2 = Worksheets("Sheet3").Range("A2")
 MyRange1.Copy Destination:=MyRange2
 Set MyRange1 = Nothing
 Set MyRange2 = Nothing
End Sub

 意味も判らず使ってたことが勉強になったことと共に、ご指摘ありがとうございます。(o_ _)o))

 ※って言ってる意味合っているのかな・・・( ̄ー ̄;A アセアセ・・・

 更に疑問点・・・
 Set ステートメントを使うのは、オブジェクト変数を宣言するときってことだから・・・
 普通にIntegerやStringで宣言するときも、モジュールレベルで宣言する場合は
 開放しないといけないってこと・・・?う〜ん(/-_-\)

 (川野鮎太郎)

 このての話はほんと苦手なんだけど、一応σ(^◇^;)の名前も出ているので
ちょっこっとだけ、、
上の鮎ちゃんの例だけど、σ(^◇^;)なら両方ともNothingって書くと思います。多分・・・
っていうかぁ、よく話題にあがってるみたいだけど、書いても書かなくてもいいみたいなのね
でも、自分でセットしたんだから開放する。つまり、忘れる??うまくいえないけど
けじめっていうかぁ、、
FindでもSetしたら、やっぱりNothing・・
残ったままじゃ気持ち悪いじゃん。
とまぁ、、σ(^◇^;)の意見でしたぁ。。。あまり参考にならないね(^^;;;
(SoulMan)

 私の場合は、プロシージャレベルの変数にNothingはしません。理由は意味がないから。

 > こんな感じでモジュールレベルで宣言した場合にのみ開放しないといけない(した方が良いかな?(^_^A;)みたいですね。
 > 普通にIntegerやStringで宣言するときも、モジュールレベルで宣言する場合は
 >開放しないといけないってこと・・・?う〜ん(/-_-\)
 モジュールレベルで宣言するってことは、その値を保持しておきたいんですよね?
 なので、必要であれば適当な値で初期化します。そうでなければ当然しないと思います。 (wizik)

 私の認識では、
 一概にどちらとも言えないようなので、
 大規模なマクロであれば、開放しておいた方が安心。
 小規模で動作に支障がなければ、開放するまでもない。
 といったところです。

 なお、すべてのオブジェクト変数に対してではなく、
 NewもしくはCreateObjectで作成したオブジェクトについてのみ対象としています。
 インスタンスが残るのが問題ということで。
  (INA)


 私なりに簡単なコードで確認してみました。
以下のようなコードを同一モジュールで組んだ場合は逆にNothingには出来ませんね。

 Option Explicit
Dim r1 As Range, r2 As Range, r3 As Range
Sub Test()
Dim MyRange1 As Range
Set r1 = Range("A1:B2")
Set r2 = Range("A4:B6")
Set r3 = Range("A8:B12")
Set MyRange1 = Worksheets("Sheet2").Range("A1")
    r1.Copy Destination:=MyRange1
End Sub
  
Sub Test1()
Dim MyRange2 As Range
Dim myMultiAreaRange1 As Range
Set MyRange2 = Worksheets("Sheet3").Range("A2")
Set myMultiAreaRange1 = Union(r1, r2)
    myMultiAreaRange1.Copy Destination:=MyRange2
End Sub
  
Sub Test2()
Dim MyRange3 As Range
Dim myMultiAreaRange2 As Range
Set MyRange3 = Worksheets("Sheet4").Range("B5")
Set myMultiAreaRange2 = Union(r1, r2, r3)
    myMultiAreaRange2.Copy Destination:=MyRange3
End Sub

 MyRange1などプロシージャ内で宣言したものは、そのプロシージャが終了した時点で開放される。
 (これはみやほりんさんの当初の意見ですね。)

 モジュールレベルで宣言した
 Dim r1 As Range, r2 As Range, r3 As Range は、
 Test1やTest2で使うため、Nothingしたらエラー

 ってことは、みなさんのおっしゃるように、時と場合による(コードの作り方)って感じで
 納得して良いのですかね( ̄ー ̄;A アセアセ・・・
 スレ主のみやほりんさんじゃないのに、いろんな方の意見、考え方が聞けて参考になりました。
 問題提起していただいて感謝します。m(._.)m ペコッ
  
ところで・・・
 INAさん (*^o^*)オ(*^O^*)カ(*^e^*)エ(*^ー^*)リーー!
 首をなが〜〜くしてお待ちしてました。_/ ̄|======○ 

 (川野鮎太郎)

 [川野鮎太郎]さん、[SoulMan]さん、[wizik]さん、[INA]さん返信ありがとうございます。
私自身はユーザーフォームでの作業や設計が多いのですが、
モジュールレベルの変数であっても、変数のクリア、開放のステートメントは書いたことがない。
たまたま必要がなかっただけでしょうけれども。大それたものは作ってないので。
私の認識としてはEnd Subまで到達してしまえばプロシージャレベル変数はクリアされる、
モジュールレベルでもモジュールがメモリから開放されたら変数も開放されるというものでした。
「もしかしたら開放しておかないとまずい場合があるのかな?」と疑問に思い確認したかったのです。
 
>インスタンスが残るのが問題ということで。
これは私の知らなかった問題です。
現状では利用していない(・・・はず)ので個人的には将来の問題として
覚えておいたほうがよさそうです。Open でテキストをメモリに展開したら Closeで開放しないと
メモリに残ってしまうのと似てそうですね。
 
ちなみに私の場合、初期値に戻すと言う意味では
変数よりもアクティブプリンタや検索設定を初期値に戻すほうに気を使っていたりしています。
大変参考になりました。
みなさん、お時間を取っていただきありがとうございました。
(みやほりん)
#INAさんお帰りなさいませ。
#落ち着いたら土産ばなしでも聞かせてください。

 なんかもう解決っていうか得心が行ったみたいですけど、一応。
単なる個人的コーディング姿勢。
 
書かない場合でもプロシージャ抜けたり、PG終了した時に
どっちみち暗黙的に破棄されると思うけど、
どうせ破棄するなら暗黙的にやるよりは明示的にやっとこうかな、という感じ。
この場合は書く、書かないっていう風に書き分けてると
書かにゃならん時に忘れそうなので(へっぽこ)
慣例として書く事が可能な限り書いておこうかな、って感じ。
 
Excelがらみのオブジェクトについて、
あまり中の事を知らない、
また、オブジェクトを使用して破棄しない場合に何がどうなるか、
っていう過去の経験が無いので、
書かないよりは書いておくかー程度の認識ですけども。
 
あとは過去の経験で、Nothingせんで居たがために
メモリ食いつぶしたり、DB接続残りまくったり、とかいう事があったので(駄目駄目)
「私はこれをもう絶対使わないんじゃー!」という場合にはちゃんと書いとこう、と。
それだけ。
(ご近所PG)

 ちょっと簡単な例を作ってみました。
A列を検索して「あ」だったら色をつける
というものなんですが、FindとForEachを使った例です。
この中に変数「i」が登場しますが、いずれの場合も
最後に
i=0
とか
i=Empty
とかは、私はしません。
でも、変数「C」や変数「MyRng」は場合によって
Nothingしています。
なぜなら、「i」は、ただの変数だけど、
変数「C」や変数「MyRng」は、色をつけたり線を書いたりできる
Rangeオブジェクトそのものだからです。
でも、当然ですが、ForEachの時にはNothingしません。
Setしてませんから、
結論からいえばINAさんやご近所PGさんの意見に賛成です。
iも
Cも
Endで
クリーされるものの
少し、意味が違う様な気がするのですがぁ。。どうでしょうか?
Option Explicit
'A列を検索して"あ"だったら、色をつける
'オブジェクト変数または With ブロック変数が設定されていません。
'とよくエクセル君が怒りますよね?
'きっとこんなことだと思います。ちょっと例が悪いかな???
Sub Macro1()
Dim C As Range, Fadd As String
Dim MyRng As Range
Dim i As Long
With Worksheets("Sheet1")
    With .Range("A1", .Range("A65536").End(xlUp))
        Set C = .Find("あ", , xlValues, xlWhole, xlByColumns, xlNext, True)
            If Not C Is Nothing Then
                Fadd = C.Address
                Do
                  Set C = .FindNext(C)
                    With C.Interior
                        .ColorIndex = 34
                    End With
                i = i + 1
                Loop Until Fadd = C.Address
            End If
    End With
End With
Set C = Nothing
End Sub
Sub Macro2()
Dim C As Range
Dim i As Long
    With Worksheets("Sheet1")
        For Each C In .Range("A1", .Range("A65536").End(xlUp))
            If C.Value = "あ" Then
                With C.Interior
                    .ColorIndex = 34
                End With
                i = i + 1
            End If
        Next
    End With
End Sub
Sub Macro3()
Dim C As Range, Fadd As String
Dim MyRng As Range
Dim Wsh1 As Worksheet
Dim i As Long
Set Wsh1 = Worksheets("Sheet1")
Set MyRng = Wsh1.Range("A1", Wsh1.Range("A65536").End(xlUp))
Set C = MyRng.Find("あ", , xlValues, xlWhole, xlByColumns, xlNext, True)
    If Not C Is Nothing Then
        Fadd = C.Address
        Do
          Set C = MyRng.FindNext(C)
            With C.Interior
                .ColorIndex = 34
            End With
        i = i + 1
        Loop Until Fadd = C.Address
    End If
Set Wsh1 = Nothing
Set MyRng = Nothing
Set C = Nothing
End Sub
Sub Macro4()
Dim C As Range
Dim MyRng As Range
Dim Wsh1 As Worksheet
Dim i As Long
Set Wsh1 = Worksheets("Sheet1")
Set MyRng = Wsh1.Range("A1", Wsh1.Range("A65536").End(xlUp))
    For Each C In MyRng
        If C.Value = "あ" Then
            With C.Interior
                .ColorIndex = 34
            End With
            i = i + 1
        End If
    Next
Set Wsh1 = Nothing
Set MyRng = Nothing
End Sub
(SoulMan)

 私の場合は、インスタンスが残る(クラスへの参照が残る) New もしくは
 CreateObject で作成したオブジェクトについてのみ Set a = Nothing しますので、
 通常のオブジェクト変数は無視してます。 

 あと、Dim o As New Class1 を使わずに Set o = New Class1 により、
 オブジェクトの生成を明示的におこない、
 暗黙のうちにインスタンスが生成されることのないようにした方が良いと思います。

 [以下ヘルプより]
 New 
 このキーワードを指定すると、オブジェクトを暗黙的に作成できます。
 オブジェクト変数を宣言するときにキーワード New を指定した場合は、
 オブジェクトを最初に参照したときにオブジェクトの新しいインスタンスが作成されるので、
 Set ステートメントを使ってオブジェクトへの参照を代入する必要はありません。

 [参考]
 Sub sample()
 Dim x As New Class1

    Set x = Nothing

    If x Is Nothing Then
        MsgBox "Nothing です。"
    Else
        MsgBox TypeName(x) & " が残ってます。"
    End If
 End Sub

 Sub sample2()
 Dim x As Class1

    Set x = New Class1
    Set x = Nothing

    If x Is Nothing Then
        MsgBox "Nothing です。"
    Else
        MsgBox TypeName(x) & " が残ってます。"
    End If
 End Sub

  (INA)

 こうなってくると、σ(^◇^;)は、ちんぷんかんぷん(?_?)なんで、
とりあえずNothingしとこぉ〜〜って感じですかね(^^;;;
ではでは、私は、この辺で退散することになろうかと思います。
v(=∩_∩=)v
(SoulMan)

 おお、なんだか思ったよりオオゴトになっているような気が・・・(汗;;
>慣例として書く事が可能な限り書いておこうかな、って感じ。
このご意見は職業プログラマさんのスタンスとして共感できます。
先ほど外注で作ってもらったAccessマクロを覗いていましたが
明らかに変数クリアしている記述が多いです。
確かに習慣的にやっていれば書き忘れは少なくなる道理。
 
[SoulMan]さんは「オブジェクトだから」というご意見ですが、
自分でデバッグしている限りでは数値、文字列、オブジェクト
・・・の変数としての適用期間の扱いは区別がないような気が
します。
ただ、[ご近所PG]さんのご意見のとおり、
>「私はこれをもう絶対使わないんじゃー!」
は大事だと思いますので、私も心がけたいと思います。
 
インスタンスの生成については・・・・
いや、もう、この話になるといまだにVBA初心者に戻ったような
感覚でして、お恥ずかしい。わかるように精進。
(みやほりん)

 もう、解決された様ですが、、、
 >数値、文字列、オブジェクト・・・の変数としての
 私は決して、みやほりんさんに意見してるわけではないので
その辺は誤解のないようにお願いします。
これは、あるひとから聞いた話なのですが、
「オブジェクトは「データ(プロパティ)」と「アルゴリズム(メソッド)」が融合したもの」
らしいですわぁ(^^;; ヒヤアセ
で、
Range("A1").Value = "あ"
とすれば"あ"がかけます。
ということは、詳しいことはわかりませんが、、
C.Value = "あ"
としてもかけるので、つまりなんらかの
「データ(プロパティ)」と「アルゴリズム(メソッド)が動いた??生成された???
だから、見た目にはなくなった様に見えても、残ってると嫌なので
自分でセットした分については、とりあえず「Nothing」しようかなって感じです。
最初に、「よく話題に・・・・」と書きましたが、ところどころで論争になっている様です。
ご近所PGさんの
 >どうせ破棄するなら暗黙的にやるよりは明示的にやっとこうかな、という感じ。
 この一言に尽きるんじゃないのでしょうか??
というわけで、、これからもよろしくお願いします。
v(=∩_∩=)v
(SoulMan)


 >おお、なんだか思ったよりオオゴトになっているような気が・・
 だって難問なんですもの。(^_^;)

 私の意見は簡単に言うと、
 = nothing ではダメなときもある。(サンプルコードで分かると思いますが。) 
 ということで(^_^;)

 >あとは過去の経験で、Nothingせんで居たがために
 >メモリ食いつぶしたり、DB接続残りまくったり、とかいう事があったので(駄目駄目)
 ちなみにADOなどでこんな使い方するとなるかも。
 Dim myCon As New ADODB.Connection
 Dim myRst As New ADODB.Recordset

 to 川野さん、みやほりんさん
 ただいまです。(^^ゞ
 南の国でバカンスしてきました。(o^^o)
  (INA)


 !?工エエェ(゚〇゚ ;)ェエエ工!? 南の島で・・・

 INAさんが居ない間にINAさんになりすまして回答していたのに・・・   ♪〜〜( ̄ε ̄;)
 自分だけバカンス・・・...ρ(..、)イイナイイナ
 (川野鮎太郎)

 >ちなみにADOなどでこんな使い方するとなるかも。
 >Dim myCon As New ADODB.Connection
書き方が悪いと再度生成されてしまうでしょうね。
その理由について、自分の理解している(つもりの)内容を元にINAさんのサンプルにコメントを入れてみます。

 Sub sample()
 Dim x As New Class1 'こういう宣言をすると、参照した瞬間暗黙的にClass1が生成される

    Set x = Nothing 'だからこの時点では確かにNothingになるんだけど

    If x Is Nothing Then 'ここでxを参照したので、この瞬間xはNothingではなく生成されてしまうため
        MsgBox "Nothing です。" 'ここには来ないで
    Else
        MsgBox TypeName(x) & " が残ってます。" 'こっちに来る
    End If
 End Sub

 Sub sample2()
 Dim x As Class1 'こういう宣言をすると暗黙に生成される事は無く

    Set x = New Class1 'こういう形でSetしない限りは実体を持たない
    Set x = Nothing 'この時点で確かにNothingになり

    If x Is Nothing Then '参照しても暗黙に生成されないので
        MsgBox "Nothing です。" 'こっちに来る
    Else
        MsgBox TypeName(x) & " が残ってます。" 'ここには来ない
    End If
 End Sub

 こんな感じ。

 >南の国でバカンスしてきました。(o^^o)
うーん優雅〜
(ご近所PG)

 当校の授業内容もかなり高度で・・・(?_?)
とりあえず、、
マカダミヤナッツちょ〜〜〜だい!!
(SoulMan)

 >NewもしくはCreateObjectで作成したオブジェクトについてのみ対象としています。
 >インスタンスが残るのが問題ということで。 
 いま簡単なマクロで試してみましたが、スコープを外れたときにTerminateが呼ばれますね。
 Nothingする必要ないように思います。
 それにNothingはオブジェクトを開放するものではないと思いますから。
 CreateObjectは使ったことないので、わかりませんが。 (wizik)

 '*****************
 ' 標準モジュール
 '*****************
 Sub sample()
     Dim x As New Class1
     If x Is Nothing Then
         MsgBox "Nothing です。"
     Else
         MsgBox TypeName(x) & " が残ってます。"
     End If
 End Sub

 '******************
 ' クラスモジュール
 '******************
 Private Sub Class_Terminate()
     MsgBox "Terminate"
 End Sub


 to ご近所PGさん
 丁寧な説明ありがとうございます。(_ _)

 to wizikさん
 >変数のスコープ外れても、インスタンスは残りますか?
 プロシージャレベルの変数でということであれば、 
 End Subで初期化されるので、残らないと思っています。
 私の場合、大規模なマクロでモジュールレベルの変数は開放してます。
 タスクに Excel.exe が残ったりするのも似たようなものでしょうか・・・?

 to SoulManさん
 >マカダミヤナッツちょ〜〜〜だい!!
 はい。ココナッツです。(・・)/◇
 http://camaro.ddo.jp/Books/coco_moon.jpg
  (INA)

おぉぉぉぉぉぉw(☆o◎)wどっひゃぁ
 この下で、、美女と・・・???
ニクイ
ありがとうございました。m(__)m
v(=∩_∩=)v
(SoulMan)

 wizikさん
暗黙的に開放されるのは間違いないと思います。
で、内部仕様がはっきりしているなら(自分が作った物だとか)その暗黙に任せても良いかもだけど、
外から提供される類の物だと中身が見えないので、ちょっと不安があるかな、と。
 
例えば(過去の実例とか検証したって訳ではなく、あくまでも例え)
DBのConnectionとRecordsetのように関連するオブジェクトの場合に
Connectionの開放時にDBとのセッションを切る動きがあるが
Recordsetが存在していると開放されない、とか
予期せぬ動作をする、とかあるかもしれない。
そうだとした場合は明示的に開放順を指示する為に
Set Recorset = Nothing
Set Connection = Nothing
と。
明示しないと期待する動作にならない可能性が怖い。
(↑繰り返すけど一応全部例え話…説得力ないな(笑))
 
なので自分は開放する時は明示する様に心がけていて、
順番が適当でも大丈夫な物についても「慣例として明示する」という感じ。
 
まぁ上記の様な考えがExcelのオブジェクトに該当する事があるのかは知らないので、
知らないからこそ自分の姿勢に従うと「明示しとくか〜」となる、かな。
 
あとは、でかい関数の途中で不要になった場合に、それ以降存在しててもメモリ食うだけだから
Nothingしておくとかいう考え方もあるかな…(そんなでかい関数作るなって話は横に置いといて)
 
 
以下はちょっと検証。
試しにwizikさんのソースを実行してみたけど、
Dim x As New Class1 の場合に Set x = Nothing しても Terminate は実行されないみたいだから、
 >Sub sample()
 >Dim x As New Class1 'こういう宣言をすると、参照した瞬間暗黙的にClass1が生成される
 >    Set x = Nothing 'だからこの時点では確かにNothingになるんだけど
ってのは嘘カモ。                              ~~~~~~~~~~~~~
(ご近所PG)

 私も ADO 使っているときは、念のため = Nothing しまくってます。
 とくに支障無かったけど。(INA)

 既に脱落しかけている相談者。
用語面が理解不足で消化不良状態ですが
 
「適用範囲(スコープ)外になれば無条件にメモリから解放される(と思われる)
プロシージャレベル変数は明示的にNothingを代入しなくても問題なさそうだ。」
けれども、
「NewやCreateObjectで作成したオブジェクトの場合はほうっておいただけでは
インスタンスがメモリから解放されない(場合がある?)または参照を解除で
きない(場合がある?)ので、明示的にNothingを代入しておくべきだ。」
 
ということでしょうか。また・・・ 
>Dim x As New Class1 の場合に Set x = Nothing しても Terminate は実行されないみたいだから、
[ご近所PG]さんの↑のコメントは
SetでNothingを代入して明示的に「参照を解除」しても作成したインスタンスは
メモリから「解放(ヘルプではこの字)」されているわけではない、ってことですね。
でも、ヘルプでは「メモリから削除」って表現もあるな。
 
#[SoulMan]さん、(ちょっと間が空いちゃいましたが)
#誤解なんてとんでもない!真剣にお付き合いしていただいて平身低頭、最敬礼です。
(みやほりん)

 私はそのような認識(結論)で良いかと思いますけど。(^_^;)

 しかし、wizikさんが仰っていた 「Nothingはオブジェクトを開放するものではない」
 というのが、一番理解するのが難しいです・・。(−−;)
  (INA)

 >「NewやCreateObjectで作成したオブジェクトの場合はほうっておいただけでは
 >インスタンスがメモリから解放されない(場合がある?)または参照を解除で
 >きない(場合がある?)ので、明示的にNothingを代入しておくべきだ。」
 参照は変数がスコープ外に出れば、当然解除されるはずです。

 私がちょっと試した限りでは、インスタンスはNewやCreateObjectしても
 すべての参照が外れれば開放されるみたいです。
 VBAでは自分でインスタンスを解放することはできない(と思う)ので、
 気にしてもあまり意味がないように思います。 

 >しかし、wizikさんが仰っていた 「Nothingはオブジェクトを開放するものではない」
 >というのが、一番理解するのが難しいです・・。(−−;)
 Sub test()
     Dim WB As Worksheet

     Set WB = Worksheets("sheet1")

     WB.Cells(1, 1).Value = "TEST"

     Set WB = Nothing
 End Sub

 このマクロを実行したら、sheet1がメモリから解放されますか?
 Nothingはオブジェクトへの参照を解除するものだと思います。

 私の認識がおかしいのかな?  (wizik)

 もうひとつサンプルを作りました。

 '****************
 ' 標準モジュール
 '****************
 Dim x As Class1
 Dim x2 As Class1

 Sub sample()
    Set x = New Class1
    Set x2 = x

    x2.s = "x2から代入しました"

    If x Is Nothing Then
        MsgBox "Nothing です。"
    Else
         MsgBox TypeName(x) & " が残ってます。"
    End If

    Set x2 = Nothing
 End Sub

 Sub sample2()
    If x Is Nothing Then
        MsgBox "x = Nothing"
    Else
        MsgBox "x は Nothing ではないよ" & vbCrLf & _
            "x.s = " & x.s
    End If

    If x2 Is Nothing Then
        MsgBox "x2 = Nothing"
    Else
        MsgBox "x2 は Nothing ではないよ" & vbCrLf & _
            "x2.s = " & x2.s
    End If
 End Sub

 '******************
 ' クラスモジュール
 '******************
 Public s As String

 Private Sub Class_Initialize()
     s = "TEST"
 End Sub

 Private Sub Class_Terminate()
     MsgBox "Terminate"
 End Sub

 まず、sampleを実行し、次に sample2を実行してください。
 sampleの最後で set x2 = Nothing としているのに、
 sample2の実行時には、[x は Nothing ではないよ]と表示されると思います。
 Class1のインスタンス自身は解放されていないということでしょう。
 Set x2 = Nothing は単に、x2の参照を解放しているのだと思います。 (wizik)

 衝突したけど、ひとまず書かせて頂きます。サンプル検証は後ほどします。

 >インスタンスがメモリから解放
 >Nothingはオブジェクトを開放するものではない
 >オブジェクトへの参照を解除するもの
 >sheet1がメモリから解放されますか?

 ん〜難しい。
 「メモリ」という言葉がまずかったですね。
 「sheet1が」というより、「WB が」かな?

 [ヘルプ:変数の有効期間の概要より]
 オブジェクト変数を宣言すると、メモリ内の領域は確保されますが、 
 値は Set ステートメントを使ってオブジェクトへの参照が代入されるまで、
 Nothing が設定されています。

 つまり、メモリは宣言した時点で割り当てられている
 (オブジェクト型なら4byte)ので、
 = Nothing によるオブジェクトへの参照(=クラス[型] への参照)の
 解除とは別のものであると・・・。
   (INA) 

 >Set x2 = Nothing は単に、x2の参照を解放しているのだと思います。
すなわち
    Dim r as Range
    Set r = Range("A1")
という場合に置き換えて考えると、
Range("A1")を格納している(メモリ上の?)領域があり、
その領域に通じる道としてrがあるのだ、と。
このrという道をNothingにした所でRange("A1")がメモリ上から消えるわけでも無いよと。これは当然。
そういった意味ではExcelオブジェクトに対するNothingは意味が無いね、って事になるかしら。
 
で、気になるのは、r自身についての状態ですが
 >つまり、メモリは宣言した時点で割り当てられている
 >(オブジェクト型なら4byte)ので、
だけど、それはずっと4byteから変化無いのものなのかしら。この辺がわからない。
 
うーん、難しいですね。ていうか理解足りてないな私。
自分もこうしておけば取りあえず安全っていう道を選んでいるだけで
内部的に細かく検証した事が無いです。
 
Nothingにこだわる根拠は、oo4oのOraDynasetオブジェクトの動作の一つに
Nothingする事でRecordsetが閉じられるよ、ってのがあったので、
http://www.int21.co.jp/pcdn/vb/noriolib/vbmag/9904/oo4o/table.html
これはこのオブジェクトがNothingされる事を前提とした作りだ、って事ですかね。
 
と、書いてて一つ参考になるかなっていうサイトを思い出しました。
プログラマのメモ帳
http://hp.vector.co.jp/authors/VA010223/
ここの「Set XXX = Nothingの本当の意味」って所。
何度か目を通してるんだけど結局「何度Nothingしても構わない」ってところしか頭に残ってなかったけど、
wizikさんのサンプルと、ここの「例4」を読むと、なるほど少し理解が深まりました。
(ご近所PG)


 おはようございます。検証してみました。 

 > Class1のインスタンス自身は解放されていないということでしょう。
 > Set x2 = Nothing は単に、x2の参照を解放しているのだと思います。
 サンプルの場合、モジュールレベルで宣言しているから
 残っているのだけではないでしょうか?

 = Nothing はクラスへの参照の解放(開放ではないですね) なので、
 インスタンス(オブジェクトの実体)の解放になると考えています。

 [ヘルプより:Set ステートメント]
  Nothing
  引数 objectvar と特定のオブジェクトとの関係を無効にします。
  引数 objectvar に Nothing を代入したときに、
  ほかの変数がそのオブジェクトを参照していなければ、
  それまでに参照していたオブジェクトに割り当てられている
  すべてのシステム リソースおよび メモリ リソースが、解放されます。 
                   ~~~~~~~       ~~~~~~~~~~~~~~      
 ここで示すリソースというのが、変数の割り当て分(4Byte)と
 生成されたインスタンスのリソースとの関係がよく分からないです・・。(--;) 

 しかしながら
 「システム リソースおよび メモリ リソースが、解放」というのは、
 インスタンスの解放と言えると思いますけど。 ヘルプの間違いでしょうか?

 set x = Nothing はクラスへの参照の解放(×開放) なので、
 TypeName 関数で、Nothingを返すということは、
 「オブジェクトを参照していないオブジェクト変数」つまり
 「クラス(変数の型)への参照が無い」ので、
 インスタンス(オブジェクトの実体)の解放になっているといえませんでしょうか?
 

 >ここの「Set XXX = Nothingの本当の意味」って所。
 これから読んでみます。・・(--;) 
  (INA)

 みやほりんさん
 >SetでNothingを代入して明示的に「参照を解除」しても作成したインスタンスは
 >メモリから「解放(ヘルプではこの字)」されているわけではない、ってことですね。
一応これはあくまでも Dim a As New 〜 という形で宣言した場合の話です。
Dim a As 〜 という形で宣言したオブジェクトに対しては有効でした。
動作が不定?っていうか、見えない、っていうか…
その変数が有効な範囲である限りは Dim a As New 〜 という形で宣言した物は
参照が残りつづけるという感じなのかも。

 wizikさん INAさん
何となく細かい言葉の問題だけで、
多分wizikさんもINAさんも理解している内容は同じなんじゃないかなーと
勝手に思ってます。
 
 >このマクロを実行したら、sheet1がメモリから解放されますか?
Sheet1はExcelのオブジェクトだからExcel終了までなくなりはしない。
つまりNothingが意味する所とは、あくまでも
 >Nothingはオブジェクトへの参照を解除するものだと思います。
 
で、モジュール内部で新たに生成したオブジェクトについては、
 >引数 objectvar に Nothing を代入したときに、
 >ほかの変数がそのオブジェクトを参照していなければ、
 >それまでに参照していたオブジェクトに割り当てられている
 >すべてのシステム リソースおよび メモリ リソースが、解放されます。 
なので
 >VBAでは自分でインスタンスを解放することはできない
かと言うと、
 >引数 objectvar に Nothing を代入したときに、
 >ほかの変数がそのオブジェクトを参照していなければ、
結果として
 >インスタンス(オブジェクトの実体)の解放になる
 
でもそれは結局暗黙的に
 >参照は変数がスコープ外に出れば、当然解除されるはずです。
この事を
 >明示的にNothingを代入しなくても問題なさそうだ。
と考えるか、
 >明示的にNothingを代入しておくべきだ。
と考えるかは作り手の判断による。
事Setのタイミングがプロシージャの最後であるならば
 >参照は変数がスコープ外に出れば、当然解除される
ので
 >気にしてもあまり意味がないように思います。
ただしNothingセットによって
 >「システム リソースおよび メモリ リソースが、解放」という
意味が
 >変数の割り当て分(4Byte)と
いう状態に戻すのならば、
そのオブジェクト変数がなにかしら参照している状態である場合には
変数の割り当て分(4Byte)以上にメモリを消費していると思われるので(これは検証してみないと解らないけど)
 >大規模なマクロでモジュールレベルの変数は開放
しておいた方が良さそうだし
 >あとは、でかい関数の途中で不要になった場合に、それ以降存在しててもメモリ食うだけだから
 >明示的にNothingを代入しておくべきだ。
って感じ?
(ご近所PG)切り貼りマニア

 ご近所PGさん。 "まとめ" ありがとうございます。(_ _)

 こんなのもありましたので、ご参考までに。
http://support.microsoft.com/default.aspx?scid=kb;ja;319832
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/modcore/html/deconshuttingdownobjectcreatedbyusingautomation.asp

http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vblr7/html/vakeynothing.asp

 メモリとシステム リソースが解放されるのは、
 アクティブな参照が残っていないことをガベージ コレクタが検出した後だけです。
                                     ~~~~~~~~~~~~~~~~~
                    ↑(−−;)?  
 .Netでない場合、メモリ管理は自動なので、意図的に解放できないということかな・・? 
http://www.atmarkit.co.jp/fdotnet/dotnettips/021gc/gc.html
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/cpguide/html/cpconprogrammingessentialsforgarbagecollection.asp
   (INA)

 さっと目を通してみました。
わたくし、頭が痛くなってまいりました(駄目駄目)
 
取りあえず前半のリンクを見る限りは
 >アプリケーション レベルのオブジェクト変数を Nothing キーワードを使用して明示的に無効にする
事は推奨されているらしい。
 
で、後半ですが…
VB(VBA)でどうなるかが良く解らないけど、
少なくとも.NETにおいては
ガベージ・コレクタと呼ばれる機構があり?
メモリ解放する行為をガベージ・コレクションと呼んでいて?
 
その行為が行う内容とは
 >例えば上記のサンプル・プログラムでは、
 >配列aが参照する配列オブジェクトは、生成された時点ではジェネレーション0に属している。
 >そのため、もしすぐに参照を解放してしまえば、次のCollectメソッドで回収されてしまうことになる。
 >しかし、この例では参照を解放することなくCollectメソッドを実行しているので、
 >解放される代わりにジェネレーションが1つ上がってジェネレーション1になる。
 >ジェネレーションが上がるということは、不要になっても回収されなくなる可能性が増えるということを意味する。
 >通常、ガベージ・コレクタはジェネレーション0を対象にガベージ・コレクションを行い、
 >それで十分な量のメモリを解放できれば、ジェネレーション1は調べないからである。
と言うような判断をして解放しているんだよ、と。
 
って事は、結局
NothingをSetしたイコールメモリの解放には繋がらない、
その後システムが解放すべし、と判断を迫られた時に、
Nothingにより参照されていない使用済みメモリ領域、不要と判断できた部分を解放しますよ、
と。
それを.NETであれば明示的に呼び出す事は出来るが、
VB(VBA)ではそういった仕組みが存在しない。
の、かしら。
(ご近所PG)

 またまた、"まとめ"ありがとうございます。
 ここまで把握しておけば、ひとまずExcel VBAを使うには十分そうですね。

 本題の   
 「プロシージャレベルの変数でも最後に開放しておかなければいけないものでしょうか。」
 については、状況によるということになってしまいますね。
            ~~~~~~~~~

 MSは、Office アプリケーションのオブジェクトのときは、Nothing しましょうと。
 >オブジェクト変数の破棄
 >プロシージャの内部で宣言した変数はプロシージャの終了時に破棄されますが、
 >オブジェクト変数を使う場合には、自動化しているアプリケーションを終了するときに、
 >明示的にオブジェクト変数に Nothing キーワードを設定してオブジェクトを
 >破棄するようにするとよいでしょう。。
http://www.eu.microsoft.com/japan/msdn/columns/office/office05042000.asp 

  (INA)


コメント:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.