2008-04-15
目次
りんご味Ruby 第23回 藤本 尚邦
■ 例外を発生させる
前回書いたYahooHonyaku (yahoo-honyaku.rb)をirbを使って試してみます。
$ irb
irb(main):001:0> require 'yahoo-honyaku'
=> true
irb(main):002:0> YahooHonyaku.translate(:ej, "hello")
NotImplementedError: NotImplementedError
from ./yahoo-honyaku.rb:14:in `_eid_'
from ./yahoo-honyaku.rb:7:in `translate'
from ./yahoo-honyaku.rb:3:in `translate'
from (irb):2
from :0
まだ機能を実装していないわけですから、まともに動かないのは当然ですね。irbでRubyプログラムを評価(実行)して例外が発生したときは、どのような順序でメソッドが呼ばれて最後に例外が発生したのかを示す情報であるバックトレースが表示されます。上のirbでのバックトレースを下から順に(ちょっと無理がありますが)日本語で書き下すと:
・irb実行の
・2番目(002)の評価から
・yahoo-honyaku.rbの3行目(YahooHonyaku.translateメソッドの中)の評価から
・yahoo-honyaku.rbの7行目(YahooHonyaku#translateメソッドの中)の評価から
・yahoo-honyaku.rbの14行目(YahooHonyaku#_eid_メソッドの中)の評価から
・NotImplementedErrorという例外が発生
というような感じになります。
(注) Rubyのドキュメントでは、クラスメソッドを「クラス名.メソッド名」、
インスタンスメソッドを「クラス名#メソッド名」と表現する習慣があります。
「Yahoohonyaku.translate」は「YahooHonyakuのクラスメソッドtranslate」 を意味し、「Yahoohonyaku#translate」は「YahooHonyakuのインスタンスメ ソッドtranslate」を意味します。
前回のyahoo-honyaku.rbの7行目は、Yahoohonyaku#translateの定義:
def translate(mode, text)
post!(:eid=>_eid_(mode), :text=>text) # 7行目
@yt_result
end
で、14行目は、プライベートメソッド _eid_ の定義:
def _eid_(mode)
raise NotImplementedError # 14行目
end
です。7行目での post! メソッド呼出のための実引数の評価で _eid_ が呼び出されて、14行目で例外が発生しています…というか「_eid_ はまだ実装していないよ」ということを示すために、あえて、raise という組み込み関数を使いRubyに組み込みの例外 NotImplementedError を発生させたのです。
と、ここまで書いてから、raise にはメッセージ文字列を引数として渡せることを思い出しました。「_eid_ はまだ実装していません」というようなメッセージを付けた方が、例外を見たとき何が起きたのかわかりやすくなりますね。例えば:
def _eid_(mode)
raise NotImplementedError, "まだ実装してねぇ、誰か書いてくれ!"
end
としておけばバックトレースが:
$ irb
irb(main):002:0> YahooHonyaku.translate(:ej, "hello")
NotImplementedError: まだ実装してねぇ、誰か書いてくれ!
from ./yahoo-honyaku.rb:14:in `_eid_'
from ./yahoo-honyaku.rb:7:in `translate'
from ./yahoo-honyaku.rb:3:in `translate'
from (irb):2
from :0
となり、これを見た気の利いた人ががかわりに _eid_ を実装してくれるかもしれないというわけです。実際には「そんなところにこだわってないでさっさと実装しろ」と言われておしまいというのがありがちのような気もしますが…
それはさておき、とりあえず外側(メソッドの呼ばれ方)だけ決めておいて、中身はあとで書くことにしよう。私はそんなときに、あとで実装すべきことを思い出すための手軽な手段として、このようなraiseの使い方をしたりしています。
もう少しきちんとしたやり方をするならば、単体テストを書くべきかもしれません。次回は、YahooHonyakuをネタにRubyでの単体テストの書き方を紹介しようかと考えています。
■ [おまけ] riコマンド
Rubyのリファレンスマニュアル(英語)は、riというコマンド(ターミナルなどから使います)を使って見ることができます。raiseのリファレンスなら:
$ ri Kernel#raise
で表示されます。組み込み関数の場合、関数名に「Kernel# 」を付けます(その意味はいずれ機会があれば書きたいところですが、今はおまじないと思ってください)。
CocoaReplでは、raiseとタイプしたところで、コマンドキーとピリオド(.)を同時に押すと、raiseで始まるメソッドの一覧がポップアップ表示されるので、raiseを選ぶと下のビューにriの結果が表示されます。
■ 参考URL
Rubyリファレンスマニュアル(日本語)のraise
http://www.ruby-lang.org/ja/man/html/_C1C8A4DFB9FEA4DFB4D8BFF4.html#raise
藤
本裕之のプログラミング夜話 #136
前回ラスト,私が10年ほど前に作ったソフトウエアの印税契約書を引用して,コンピュータ・ソフトウエアがああいう風に「広義のソフトウエア」と同じように扱われるのはいいことだったのかと書いた。当時はいいと思ってたんだけど、と。
そうなのだ。当時のワタシはそれをなんとなくいいことだと思っていたのである。出来れば消してしまいたいような恥ずかしい過去のことを「黒歴史」と呼ぶんだってのを最近教えてもらったのだが、思い返せば当時のそういう「見解」のなかには、「コンピュータ・ソフトウエアが工業製品ではなくて『作品』として評価されることで、それを作った自分もなんだか『職人』から『作家先生』に格上げされるような気がする」という、夜郎自大というかどこかさもしい意識があり、それがプログラマに最も大切な「事実を事実として見る眼」が曇ってしまっていたのではないかと……思うのね。
でもこれ、何より経済的に成り立たない話だったのである。つうか、オレもそうだったかも知れないけど「作家扱いされて浮かれるプログラマは、結果的にワリを食う」構造なのである。ここ、核心なので一言一句噛みしめつつ丁寧に読んで欲しい。いいっすか?
文芸あるいは音楽作品など「コンピュータ・ソフトウエア」ではない「ソフトウエア」は「ハードウエアからの独立性」を持っている。が、「コンピュータ・ソフトウエア」はそーぢゃない、んである。
具体的に言えば分かりやすいか。
例えば筒井康隆著「時を駆ける少女」はまず、学研の学習雑誌「中三コース」から「高一コース」に7回に渡って連載され(1965年〜66年)、盛光社というところから単行本が出たあと角川文庫になり、以降何度かカバーなどを刷新して増刷されているうえ、新潮社が出した「筒井康隆全集第4巻」にも収録されている。テレビ・ドラマや映画は別として、紙媒体で供給される「時を駆ける少女」は基本的に同一のものであり、オレ自身ウチの本棚から全集第4巻を引っ張り出せばいつでもあのラヴェンダーの香りを味わうことができる……というのはちと詩的に過ぎますか。
例えばビートルズが1966年に発表した名盤「サージャント・ペパーズ……(全部書こうかと思ったがみんな知ってるだろうし長くなるだけだからやめておく……ってこの言い訳の方が長いか)」。ワタシは以前これのLPレコードを持っていた、が、使っていたレコードプレイヤーが完全におしゃかになった1995年ごろ、CD版を購入、LPは近所の中古レコード屋さんに売った。CD版は「A Day In The Life」のあとに入っているおふざけ音源がリピートするところがLPと違うのだが、他は同じ(オレはLPとCDの音質の違いを云々できるようなオーディオセットを持っていない)である。今度 iTunes Storeでも買える
ようになると聞いたが、あの部分はどうなるのかな?
これらに比べて先ほどから話題の、10年ほど前……正確には12年前だ、1996年にワタシとその仲間が発表した「Alt Go Key」というコンピュータ・ソフトウエア。「時を駆ける少女」や「サージャント・ペパーズ」に遅れること30年のこのソフトウエアは、その動作環境が「68LC030-33MHz以上のCPUを搭載し、漢字Talk7.1が稼働するMacintosh」あるいは「Pentium75MHz以上のCPUを搭載し、日本語Windows ’95が稼働するIBM社製PCおよび互換機」であり、現在ウチにあるどの機械をもってしてもプレイできないのである。売れなかったから、売ってないからぢゃなくて、ここにそのソフトはあるのに動かないのだ。私の言う「ハードウエアからの独立性」って分るでしょ?
もちろんエクセルやファイルメーカー、あるいは一太郎などのように、ハードウエアの変遷に応じて動作し続けるようバージョンアップを繰り返すソフトウエアも存在する。でもそのバージョンアップにはコストが掛かるのであり、誰かがそのコストを負担しなければならない。「時を駆ける少女」や「サージャント・ペパーズ」が基本的に最初に書かれた、録音されたままの状態で消費されるのと比べたら雲泥の差でしょう? そう、問題にしたいのは「コスト」なのである。なぜってこの「コスト」の中に、オレ達プログラマの「食い扶持」も含まれるわけだから。
(以下次回 2008_04_11)
高橋真人の「プログラミング指南」第134回
〜XcodeによるPowerPlant X入門(23)〜
こんにちは、高橋真人です。早速続きを始めます。
前回は、PPx::PushButtonのサブクラスを作るための準備として、今までの青いボタンを黒い枠に変更しました。今回はさらに変更を加えていきます。
それでは、今度は以下の変更を加えてください。
【変更の対象:MyView.hのクラス定義】
《変更前》
#include
【変更の対象:MyView.cpのInitialize()】
《変更前》
BaseView::Initialize(inSuperView, inFrame, inVisible, inEnabled);
《変更後》
PPx::PushButton::Initialize(inSuperView, inFrame, inVisible,
inEnabled, CFSTR("Button"));
【変更の対象:MyView.cpのDoControlDraw()】
《変更前》
return noErr;
《変更後》
return eventNotHandledErr;
以上です。
最後のところは少し分かりにくいかもしれませんね。前回修正を加えた関数の最後のreturn文の返す値をnoErrからeventNotHandledErrに変えるだけです。
説明はすぐにしますので、とりあえず走らせてみてください。
前回表示された四角い枠の中にボタンが表示され、Buttonという名前が付いていれば成功です。実行環境によってボタンの見た目が違う場合があるのですが、この点については今回は大きな問題ではないので、とりあえず気にしないでください。(いずれ、この点についても言及します)
さて、DoControlDraw()という関数が、Viewの見た目を表現するためのものであるということはお分かりになると思いますが、前回の四角い枠だけの状態から、関数が返す値を変えただけで、何故ボタンが表れるようになったのでしょうか?
MyViewの継承元であるPPx::PushButtonというクラスは、元々一般的なボタンのためのクラスです。ですから、今回のようなサブクラスではなく、PushButton自体のインスタンスを作った場合、Viewはボタンとして見えているのです。
前回は、その辺の前提を踏まえずにいきなり、「ボタン自身の描画処理」をブロックするようなコード実装をしてしまったために、DoControlDraw()の中で呼び出したCGContextStrokeRect()が描いた四角い枠だけが見えたのです。
一般的にオブジェクト指向の仕組みにおいて、サブクラスが継承元のクラスの処理をそのまま流用する場合には、サブクラスでオーバーライドした関数(メソッド)の中からスーパークラスのメソッドを呼びます。PPxであれば、
PPx::PushButton::DoControlDraw(/*引数は省略*/);
としますし、Objective-Cであれば
[super doControlDraw.....];
といったような書き方になります。
しかし、今回MyViewが継承しているPushButtonの場合、呼び出そうにもPushButton自身はDoControlDraw()を持っていないのです。そこで、今回の修正が行きてくるわけなのです。
では、今回加えた変更は何を意味するのでしょう?
連載の第118回目で、以下のような解説をしています。
こんな感じに自分に関心のあるイベント&コマンドの時にのみnoErrを返し、それ以外の時にはeventNotHandledErrを返すのです。Carbonイベントの仕組みでは、ハンドラがeventNotHandledErrを返した場合は次の処理候補に処理を依頼するようになっています。
何のことを言っているのか、全く分からない方は過去の記事を読んでみていただきたいのですが、ここで言っている「自分の関心のある」ということは、裏を返せば「関心がない場合は何もせず、ただ単にeventNotHandledErrを返す」ということになります。そうすることによってイベントはさらに引き継がれて行って、そのイベントに「関心のある」ハンドラに行き着いた段階で処理されることになるのです。
そのことは別の見方をすれば、イベントをさらに先に伝えていきたい場合にeventNotHandledErrを返し、もうこれ以上、先に伝えたくない、つまりここでイベントの伝搬を終わらせたいと思えばnoErrを返してやればいいということになります。
今回のプログラムでの変更はこの仕組みを利用しています。
つまり、“自分の”DoControlDraw()の中では単に枠だけを描くわけですが、そこであえてイベントの流れを止めないことで、それが最終的にPushButton本来のボタンを描く処理にまで行き着くことを狙ったのです。
さて、何故ボタンが表れたのかがわかったところで、いま一度プログラムを動かして、少し観察をしてみてください。今説明した描画の仕組みのために、MyViewではちょっと困ったことが起こっているのです。
何が困っているのかと言うと、ある特定の状況で表示にちょっとした違いが出てしまっているのです。お気づきになりましたか?
ヒントとしては、ウインドウをアクティブでない状態に置いた時に表示状態が変わる、ということです。もちろん、「ボタンが薄く表示されるようになる」ということを言っているわけではありませんよ。
次回は、この点について調べてみようと思います。
開発ツールよもやま話 MallocDebug 後編 高橋 政明
パフォーマンスツールであるMallocDebugの説明を続けます。
◆メモリリークを見つける
前回に引き続きAppleデベロッパサイトMemory Usage Performance Guidelines
の Finding Memory Leaks にMallocDebugを使ったメモりリークを見つける
方法が載っていました。
http://developer.apple.com/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html
1. MallocDebugを起動する
2. 新規ウインドウを開き、分析したいアプリを選択する
3. Launchボタンをクリックして分析したいアプリを起動する
4. 分析したいアプリを動作させる
5. MallocDebugのモードを“Leaks”にする
6. リーク状況がウインドウ中央のブラウザ部分に表示される
これはMallocDebugを単独で使う場合の手順です。
Xcodeの「実行」メニューの「パフォーマンスツールを使って開始」階層メニューの「MallocDebug」を選ぶとターゲットのアプリを選択した状態となるので便利です。(1.と2.がワンタッチで完了します)
「どこで」「どれだけ」メモリーをアロケートしたかがブラウザ部分で追跡でき、デバグビルドであればソースアイコンの付いたメソッド名をブラウザ部分でダブルクリックするとそのソースがXcodeで開きます。
5.の“Leaks”は Figure 1 MallocDebug main windowの「Analysis Mode」と書かれたポップアップボタンです(MallocDebug起動時は「All Allocations」が選ばれていています)。このポップアップボタンは 3.で分析したいアプリを起動するまではディスエイブル状態です。
なおブラウザ部分に表示されている分析結果は 5.で“Leaks”を選んだ時点のものです。分析したいアプリを操作した後や動作した後の状態を分析するには「Update」ボタンをクリックして再度分析してください。
ブラウザ部分の階層が深い場合次のメニュー項目が便利です。
Edit>Descend>Max Pathで最も深いパスを表示します。
Edit>Descend>Unique Pathは複数にわかれているパスを表示します。
◆不必要な多量のメモリのアロケートを見つける
多量のメモリを確保すると仮想記憶スワップによるディスクアクセスが発生し性能が落ちる可能性が高くなります。MallocDebugは起動時からの確保したメモリを表示しますが「Mark」ボタンを使うと特定の時点以降のメモリ確保だけを注目できます。
まず「Mark」ボタンをクリックします。次に分析したい処理を行います。それからポップアップの「Allocations from mark」を選ぶと「Mark」クリック以降に確保し解放していないメモリだけを表示できます。例えば「Mark」をクリックしてからウインドウを開くと、その処理に関連するメモリだけを表示できます。
◆MallocDebug解析中のクラッシュ
http://developer.apple.com/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingPatterns.html
の Listing 1 がクラッシュ時に表示されることがあるそうです。これは解放されたメモリを参照している場合で発生します。MallocDebugはメモリを解放するとき0×55で埋めるのでクラッシュ時の参照先が0×55であればメモリ参照のバグだと確認できます。
Objective-C 2.0のガベージコレクション機能が追加されました。残念ながら現実的にはガベージコレクションを使える局面はまだ大部分とは言えない状況と思われますのでMallocDebugはこれからもしばらくの間は必須のツールとして活躍しそうです。
◆本日のまとめ
MallocDebugを使いメモリ確保状況を把握しよう。
◇MOSAからのお知らせと編集後記は割愛します◇