[[20101124111503]] 『Set と Nothing の使い方』(なた) ページの最後に飛ぶ

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

 

『Set と Nothing の使い方』(なた)

 いつも勉強させていただいています。なたと申しまおす。

 エクセルファイルが重くなっていく原因のひとつなのかと
 思い、質問させていただきました。

 Set を Nothing する前に Exit sub がある場合なのです。

 以下のコードの場合、メモリを食っちゃう原因になりますか?

 With ThisWorkbook.Worksheets(1)

     myDelCOL = .Rows(1).Find("ID", , , xlWhole).Column
     Set s = .Columns(myDelCOL).Find(DELETE_ID, , , xlWhole)

     If Not s Is Nothing Then

         MsgBox "削除予定IDで処理されている商品があります。",,"削除中止"

         Call Module2.商品一覧作成(DELETE_ID)

         Exit Sub             <--------------------コード終了させたい。

     End If

     Set s = Nothing          <--------------------Nothingがあとにきてる。

 End With

WindowsXP Excel2007です。


 > エクセルファイルが重くなっていく原因のひとつなのかと
 > 思い、質問させていただきました。

 話は別だと思いますよ
 Range等はNothingしなくても、大丈夫だと思いますが?
 使ったObject変数は、使い終わったら破棄すると言う事を
 明示的に行っているから(心配な部分を在りますが?)そうしているだけだと思います

 >Set を Nothing する前に Exit sub がある場合なのです。
 >
 >以下のコードの場合、メモリを食っちゃう原因になりますか?

 ただ書き方が悪いだけでは?

     With ThisWorkbook.Worksheets(1)
        myDelCOL = .Rows(1).Find("ID", , , xlWhole).Column
        Set s = .Columns(myDelCOL).Find(DELETE_ID, , , xlWhole)
        If Not s Is Nothing Then
            MsgBox "削除予定IDで処理されている商品があります。", , "削除中止"
            Call Module2.商品一覧作成(DELETE_ID)
            'プロシージャを呼び出しているのだから、ここへ戻って来るので
            Set s = Nothing
            Exit Sub             '<--------------------コード終了させたい。
        End If
 '       Set s = Nothing '< --------------------不必要でしょ!、上のIf文でNothingで無ければ引っかけています
                        '                      当然sはNothing
    End With

 (Bun)


 > Set を Nothing する前に Exit sub がある場合なのです。
 > 以下のコードの場合、メモリを食っちゃう原因になりますか?

 メモリの問題として質問されていますが、
 Set Nothingしなければいけないのかどうかが、最優先です。

 変数sが、モジュールレベルの変数なら、Bunさんのアドバイスに従って確実に参照を断つ必要があります。

 プロシージャレベルの変数なら、今のままで問題ないと思います。set Nothingが余分な位です。
 世の中とソフトは、理屈どおりに動かないことがあるので、保証はしかねますが。。。

 「重い」の意味をもう少し説明いただく方がいいと思います。トラブルシュートの着眼点が微妙に変わります。
  「ファイルサイズが大きくなって読込むのに時間がかかる」
  「データを変更すると計算に時間が掛る」
  「メモリが少ないので、メモリをちょっとでも余分に使うとマクロの終了が遅くなる」

 (半平太) 2010/11/24 13:57

 参考過去ログ。
 
[[20041124212113]]『VBA:変数の開放の必要性』(みやほりん)
 
(純丸)(o^-')b

Bunさんありがとうございます!

 そうなんです。そこにSet s = Nothing を入れたいのですが、

 If Not s is Nothing Then

     Set s = Nothing  <----------------ここって、If文の、"Sがあったら"と言う場所なので、
                                       ここで解放してしまったらエクセルが混乱するかなー
                                       と思っていました。
 End if

 半平太さんありがとうございます。
 ほとんどプロシージャレベルなので、だいぢょうぶな気がしてきました(^^;
 「重い」というか、私のテスト環境では問題ないのですが、CPUやオフィスの
 仕様が違うパソコンではエラーがでるのです。

 参照が出来ない系(.Findメソッドとかで、あるのに見つからずにrangeエラー等
 のエラーがたまに出るので、原因を調べたくて。。

 純丸さんレスありがとうございます!
 そのスレみたのですが、よくわからなくて(^^;

 失礼しまーした!m(__)m

 > 参照が出来ない系(.Findメソッドとかで、あるのに見つからずにrangeエラー等
 > のエラーがたまに出るので、原因を調べたくて。。

 それは、全然違うお話です。

 正確にはどんなエラーメッセージなんですか?

 私がFindメソッドで見つからないケースをトライしたら、
 「実行時エラー'91' オブジェクト変数またはWithブロック変数が設定されていません」
 と出ましたけど?

 (半平太) 2010/11/24 17:05

多分そんな感じのです!

もう一回調べてみますー!

なた


 再掲します。
 ↓
 > メモリの問題として質問されていますが、
 > Set Nothingしなければいけないのかどうかが、最優先です。

 Nothingにすべきではないのに、Setしちゃったのではないですか?
 オマジナイみたいに書いていると、そんなトラブルになっちゃう人がいるんですけど。

 モジュールレベル変数でそんなんことが起きていないか?
 引数を渡されて動くプロシージャでそんなことをしていないか?

 それを中心にチェックしてみるといいかも知れません。

 (半平太) 2010/11/26 09:14

 半平太さんレス感謝です。

 コード自体に問題があれば、いつもエラーになると思うのです。
 環境によるみたいなのと、同じ環境でもエクセルの再起動後には普通に動くので、
 メモリーを食っちゃう書き方をしているのかな。と思っていました。

 引数を渡される動くプロシージャたくさんありますが、そこでSet使ってわいけないのですか?
 かなり修正しなくちゃ。。。

 (なた)


 > 引数を渡される動くプロシージャたくさんありますが、そこでSet使ってはいけないのですか?
 > かなり修正しなくちゃ。。。

 そんな形式論的なことを云っておりません。

 用済みになったものなら、NothingをSetするのは何も問題ありません。(・・・と此処では云って置きます)

 もし、あとでそのまま使いたいオブジェクトなら、Nothingにするのはマズイです。
 (そんなとき、メモリーを食うとか云ってられないでしょう?)

 そのどっちであるのか見極めが出来るのは なたさん しか居ません。

 (半平太) 2010/11/26 20:05

 >同じ環境でもエクセルの再起動後には普通に動くので、
 > メモリーを食っちゃう書き方をしているのかな。と思っていました。

 この現象で一つ思い当たるのは、開いている他のブックに対して、
 または自ブックの他シートに対して、クエリを実施することですね。

 これをやると、メモリーリークが起きて、何回か繰り返していると
 メモリー不足に陥る(らしいです)。

 (半平太) 2010/11/26 20:31

 このエラー原因と直接の関係がないと思いますが、
 『Set と Nothing の使い方』という表題だったのでちょっとだけ・・・。

 まず、オブジェクト変数Rがモジュールレベルの変数だろうとプロシジャーレベルの変数だろうと

 プログラマがコード内できちんと
 set r=nothing

 と記述すべき という立場からの意見です。

 なんていっても私も良く忘れます、実は・・・。
 もっともお仕事では、コードレビューをしてもらうのでこれも殆ど修正されますけど・・・。

 理由は、

[[20090303232447]]

 ↑これ。

 更に具体例として

[[20100624190216]]

 もっともこの時もキリキさんには、理解してもらえませんでしたが・・・。

 メンテナンスの面から、Set R=Nothing

 をいれておくべきだ という考えです。

 後々のメンテナンスのことを考えて、

 Goto文は 使わない
 Public変数は 使わない
 プロシジャーに出口を二つ作らない

 なんていう鉄則じみた項目が構造化プログラミングのドキュメントでは、
 見かけます。
 実際には、GOTO文を使って又、Exit Subを使っても可読性のよいコード書く方は
 私も見かけます。
 (例えば、Bunさんのコードなんて、Goto文を使っていますが、わかりやすいコードですよね?
 Bunさんって、VBA質問箱のHirofumiさんですか?
 違ってたらごめんなさい、コードが良く似ているから・・・。)

 それでもプログラミングを始めて間もない方には、上記の3点は推奨します。
 それの方が間違いがないと思うからですが、
 Set R=Nothing もそれと同じ理由から付けるべきです。

 >引数を渡される動くプロシージャたくさんありますが、そこでSet使ってわいけないのですか?

 オブジェクト変数を他のプロシジャーに渡す場合は、特に理由がない限りは、
 値渡しで渡してください。

 Sub test1()
    Dim rng As Range
    Set rng = Range("a1")
    Call proc(rng)
    MsgBox rng.Address
    Set rng = Nothing
 End Sub
 Sub proc(ByVal r As Range)
    MsgBox r.Value
    Set r = Nothing
 End Sub

 値渡しにしておけば、例えコード間違いで
 Nothingにしても 呼び出し元では影響がないので・・・。

 最後に、間違い探しのご質問なら、
 まず、再現手順書が必要ですよ!!
 コードだけでは、足りません。

 質問者は、閲覧者を自分の抱えている問題点まで導かなければなりません。
 そのためには、
 入力データを提示すること
 これは、具体的な例を提示してください。

 理想の結果提示と現状の提示
  これも入力データに基づいて提示してください。

 これね、結構記述が大変ですが、プログラミングをするなら、きっと
 役に立ちますから・・・。

 私もこのエラー原因には、興味があるので是非解明してください。

 ichinose


 > 理由は、
[[20090303232447]] 
 > ↑これ。

 あれから、米国ならこんな問題、とっくに結論が出ているのではないかと思い、
 調べたことがあります。しかし、日本と大差ない状況にあるとの認識です。

 ただ、マイクロソフトのスタッフで、私と同じ意見の人が居たので
 意を強くしております。(中身の厚さが違いますけどね)

 When Are You Required To Set Objects To Nothing? 
http://blogs.msdn.com/ericlippert/archive/2004/04/28/122259.aspx

 英語だし、私の都合のいい様に解釈している傾向もあるので、
 正確とは云えませんが、私の読むところ以下のようでした。

   用済みのオブジェクト変数にNothingをsetするのは有益だ。
   しかし「End Subの直前でローカルオブジェクト変数に実施するのは無益だ。」

   なのに、巷間、この無駄ステートメントが氾濫している。
   ステートメントの意味も分からずに書いている「猿まねプログラマー」の
   仕業とばかりは言えなさそうだ。誰がどんな理由で流行らせているのだろうか?
   
   バグ対策と云う話は聞いたことがある。
   しかし、それならそのバグに対応するだけでいい。
    あつものに懲りてなますを吹く様なことはすべきでない。

   そんなに徹底したいのなら、なぜ普通の変数は初期化しないでいいのか、
   その理屈をお聞きしたいものである。

 この主張に対して、実務家から多数の反論が出ています。

 しかし、大半はADOオブジェクトのバグが論拠です。

 ポリシーと云う人がいましたが、それは会社のポリシーだから。
 プログラマー達は、必要性を納得していない。

 毛色の変わったところでは、Nothingになるのはいつのタイミングなのか説明しろ、
 そう云われると窮するので明示的に書く、それなら歴然としているから、と云う人がいました。
 これは、Nothingするにも順序があると云うことだと思われます。
 「書くが、だた書けばいいと云うものではない」と云うのは、私には凄い話です。
 これだと、システム的にNothingをセットすることは出来ない。
 でも本当にそんなこと判断して書いている人っているのかしら??ですけどね。

 以上を踏まえ、現時点で私は、「どうせ終了するのだから同じことである。ただし、
 バグがらみのADOオブジェクトとアプリケーションオブジェクトはSetする」とのスタンスです。

 (半平太) 2010/11/27 00:23

 私は、現状あるバグに対する対処として、Nothingをセット
 なんていう一時的な話で言っているのではないです。

 >バグがらみのADOオブジェクトとアプリケーションオブジェクトはSetする
 これだと、このバグが解消されれば、Nothingは要らない ということですよね?
 >なぜ普通の変数は初期化しないでいいのか、
 本来は、この意見が正しいですね、全てやるべきなんでしょうが、
 特にオブジェクトに関しては、オブジェクト内には、プログラムがあるからです。

 sub test()
 dim a as new class1
 dim b as new aaa '適当なクラス
 dim c as new ccc

 '何らかの処理

 end sub

 前にも言いましたが、オブジェクトは、プログラムですから、バグはあってもおかしくないんです。これは、中を隠蔽しているんでから、ないなんて言い切れません。
 Terminateイベントでバグがあるかもしれないですよね?

 この場合だと、End Subでエラーになってもどのオブジェクトなのか
 このコードでは、わからない可能性があります。

 sub test()
 dim a as new class1
 dim b as new aaa '適当なクラス
 dim c as new ccc
 '何らかの処理
 set a=nothing
 set b=nothing
 set c=nothing
 end sub 

 とすれば、もし、cのオブジェクトの問題があれば、
 set c=nothing  ここででエラーになるし、
 aのオブジェクトに問題があれば、Set a=nothing ここでエラーになります。

 それ以外の原因なら、やはりEnd Subでエラーになるのでしょう。

 原因究明が少しでもはやくなる、メンテナンスがしやすい
 という理屈なんです。

 End Subでのエラーで最初からオブジェクトに原因があるとは限りませんからね!!

[[20100624190216]]

 ここでその事を例に取り上げています。

 >Nothingになるのはいつのタイミングなのか
 Withの場合、このNothingのタイミングが特定ではないなんて例を
 先日教えてもらいました。

 ですから、こんな意見もあり得るんでしょうが、
 私がNothingを明示的に の理由は、メンテナンスに重点を置いているからです。

 ichinose


 半平太さんの紹介記事、面白かったです。意訳も適切な気がしました。

 話は変わりますが、最近、VBS に代わって PowerShell を使い始めていますが、
 便利になってますね。オブジェクトの代入は Set 自体が不要になっていますし
 文法もかなり便利に拡張されている気がします。

 一応 VBS への変換をサポートしているようですが、昔の C と C++ の関係の
 ように本来の活用をするには、きちんと言語仕様を理解したほうがよい感じが
 しました。

 Mac の EXCEL はスクリプトは JavaScript 系に移行してしまったようですし、
 最近の多くのアプリケーションがスクリプトといえば JavaScript的な文法が
 標準になってきていることを考えると、VB の言語自体衰退しているようにも
 感じます。

 ScriptingGuy もPowerShell へ行ってしまったようですしw、Office の
 エンジンも早晩、そっちに移行しそうな気がしています。
 そうなると、Nothing 論も過去の遺物になるかもしれません。

 駄レス、失礼しました。
 (Mook)

 レスありがとうございます。

 >この現象で一つ思い当たるのは、開いている他のブックに対して、
 >または自ブックの他シートに対して、クエリを実施することですね。
 >
 >これをやると、メモリーリークが起きて、何回か繰り返していると
 >メモリー不足に陥る(らしいです)。
 >

 クエリたくさん実施してます(><)
 きっとこれが原因かもですね。
 ありがとうございます。。。

 なた 


コメント返信:

[ 一覧(最新更新順) ]


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