MOSA Multi-OS Software Artists

MOSAはソフトウェア開発者を支援します

  • iPhone/iPod touch アプリ紹介
  • MOSA掲示板
  • 活動履歴
  • About MOSA(English)

MOSA Developer News[MOSADeN=モサ伝]第343号

2009-05-19 

目次

  • りんご味Ruby         第47回  藤本 尚邦
  • 藤本裕之のプログラミング夜話   #160
  • 高橋真人の「プログラミング指南」  第158回

りんご味Ruby   第47回  藤本 尚邦

引き続き、Ruby+RubyCocoaを使って「CoreGraphicsによるPDFの描画」を記述するための簡易言語(DSL)についてです。

beginPage と endPage は、何らかのプログラム片を挟んで、順番に呼ぶことを前提として用意されたメソッド(CoreGraphics の API)です。前回は、この2つのメソッドの組み合わせを page という1つのメソッドにまとめました。その肝となるのはブロック構文です。今回はちょっと脱線して、そのブロック構文のメリットや使いどころなどについて考えてみることにします。

CGContextBeginPage のリファレンスマニュアルには:

you should call this function together with CGContextEndPage …

とあります。「あなた(プログラマ)は2つのAPIを一緒に使うべきだ」というわけです。とはいっても CGContextEndPage を忘れてしまうかもしれませんし、実際のところ、このルールが守られるかどうかはプログラマに委ねられています。その上、(使うべき) CGContextEndPage を使わなかったとしても、コンパイルやリンクでエラーになるわけではありません。プログラムを実行してみて
初めて何かおかしいと気づくことになるでしょう。あるいは気づかずにそのままになっているかもしれません。

そんなことなら、一緒に使うことを強制するようなAPIを用意した方がよさそうなところですが、実際にはそのようなAPIがないのは、(残念ながら) C にしても Objective-C にしても、任意のプログラム片をデータとして扱う手段が弱いからでしょう(ないわけではないのですが)。

任意のプログラム片をデータとして扱うことができることにより、その「任意のプログラム片」を仮引数(以下の例ではblockという名前です)として受け取る:

 def page(&block)
   beginPage
   block.call(self)
   endPage
 end

というようなメソッドを定義することが可能になります。このようなメソッド(あるいは関数)自体は、C や Objecive-C でも定義することはできます:

 void CGContextPageDo(CGContextRef context, void (* block)(CGContextRef))
 {
   CGContextBeginPage(context);
   block(context);
   CGContextEndPage(context);
 }

しかし、違いが出てくるのは呼び出し時の使い勝手です。上記の C言語バージョンのpageを呼び出す場合は、その都度 block に渡す関数を定義することになるでしょう:

 static void draw_page_1(CGContextRef context) {
   CGContextSetRGBFillColor(context, 1.0, 0.0, 0.0, 1.0);
   CGContextAddArc(context, 300, 300, 100, 0, 2 * 3.14, 1);
   CGContextFillPath(context);
 }
 /* ... 中略 ... */
 CGContextPageDo(context, draw_page_1);

この draw_page_1 みたいな使い捨てに近い関数は、どんな名前をつけるべきかということを考えるだけでもちょっと困ってしまいます。結局は、このようなAPIがあっても使うのが面倒くさいので、自力で beginPage と endPage で挟む方法を選択することになりそうです(そしていずれ endPage を書き忘れます)。

2つのAPIを1つにまとめたメリット(endPageが呼ばれることを保証する)を保ったまま、この面倒くささをなくすのにうってつけなのが、Rubyの場合はブロック構文なのです。付け加えると、JavaScriptならば関数リテラルで、Lisp系言語ならばラムダ式です。これらはみな、その場限りのプログラム片(無名の手続き) をデータとして扱うための手段という意味では似たようなものです。

すでに見てきているようにRubyのブロック構文を使うと、C言語の例でdraw_page_1 として定義した部分をブロックにして:

 pdf.page do 
   pdf.setRGBFillColor(1.0, 0.0, 0.0, 1.0)
   pdf.addArc(300, 300, 100, 0, 2 * Math::PI, 1)
   pdf.fillPath
 end

のように書くことができます。2つのAPIをまとめるメリット自体は明らかだと思うので、使い勝手が十分であればこのようなAPIがあればその方がいいですよね。このパターンにはまるAPIやクラス(例えばNSAutoreleasePool)は多いので、Objecitve-Cも、さらに構文を拡張するのならブロック構文は入れてほしいところです。

次回は、CGPDFContext.file を実装して、簡易PDF描画言語DSLをどうにか完成させる予定です。

藤本裕之のプログラミング夜話 #160

 ゴールデンウイークのせいで一週余計に間隔があいたこともあるし、ここで一度、寄り道の多かったこの1月からの話を整理してみたい。

 1月、年末年始に列島を吹き荒れた派遣切りの風にあおられつつ(かっこいいな、表現が小説みたいである)不祥フジモトが考えたのは、そういえば我々プログラマを含む「ソフトウェア技術者」というヤツは、80年代に施行された最初の「労働者派遣法」のときから「高度で専門的な知識と経験を要する職業」として、その対象だったんだよな、ということであった。

 その後、派遣法は幾度か改正(なにが改「正」だという意見はあろうが、一応そういう用語なので)され、みなさんご案内の通り「企業がいつでもクビにできる安価な労働力を調達するための便利な法律」に変質し、いままさに「派遣切り」「格差拡大」などの問題を生み出しているわけである。

 が、派遣法がそうなった……つまり当初の「高度で専門的な知識と経験を要する職業」からその対象を拡大したからと言って、「ソフトウェア技術者」が「高度で専門的な知識と経験を要する職業」でなくなったわけではないのに……ないよね? なのになんでまたこうも「ソフトウエア技術者」が食えなくなってきてるんだろうか?

 え? 「オレは食えてる」って? そりゃオレだってなんとか仕事はあって今日明日ホームレスになりますってわけぢゃないが、それでもタカハシ編集長が本稿のテーマとして「アプリケーション・プログラマは絶滅に瀕しているのか」というのをオレに示し、オレの方でもなるほどその問題について考えてみましょうかと思って不思議でないくらいの「そういう雰囲気」ってのは共有されているではないか。いない?

 もちろん「♪好景気を知らない子供達」だったりする今の若い人は「♪青空が好きで花びらが好きでなんとか食えてりゃ充分ぢゃないですか」と言うかもしれないが、「なんとか食えてる」のと「景気がいい」のは違うんであり、オレはたまたま(自業自得という意見もある)そうなってないが、オレ達が20代、30代だったときには、ほとんどのヒトが「普通の将来」として「結婚して子供の2人も作っていずれはマイホームも」てなことを想像していたし実現したヒトもいたのだよ。今の20代でそんな将来を確信できるヒトってどんだけいる?

 でね、世の中全般はともかく、ソフトウエア技術者に限って言うとそれはヘンぢゃないか、と。

 そりゃ我々のシゴトだって(バブルが弾けた話のときに書いたように)景気の悪さと無縁ではないが、でもなんか理論値・計算値以上に割を食わされているような気がする。いくら百年に一度の不景気(この「百年に一度」というキャッチはまったく根拠不明だよな……だってまだ大恐慌のときみたいにマンハッタンのビルからヒトが降ってないぢゃん、ねぇ?)とは言え、コンピュータリゼーションはその進行を止めたわけではないし、インターネットは隆盛だ。技術者の需要は拡大こそすれ縮小しているとは思えない。

 なのにあなたは京都へ行く……ぢゃない(そもそも古すぎだよな、このギャグ)、なのにその「繁栄」の果実は我々のところに落ちてこない。ここ10年くらいの間、幾人もの「IT社長」がその栄華を誇り、芸能人と浮き名を流し、国会議員に立候補したり逮捕されたりしたが(いや、逮捕はされたくないけどさ)、オレの知る限り一人たりとも「現役のエンジニア、プログラマ」ではなかった。昔、少しはHTMLとか書いたコトのあるヤツはいたかもしれないけど、基本的には「経営者」であり「マネージャー」なんだよね。

 彼らは儲けるがオレたちはそーでもない。いや、却って苦しくなったりしてる。

 これってなんだか俺たちのアズカリシラヌところで、さっき「そんなことあるわけないぢゃん」と否定したトコロの「『ソフトウェア技術者』が『高度で専門的な知識と経験を要する職業』でなくなる」という怪現象が、実際に起きてるみたいではないの? ではそれは、どこでどんな理由で起きてるのだろうか?
                       (以下次回 2009_05_15)

高橋真人の「プログラミング指南」第158回

これから始めようとする人へ(9)

 皆さんこんにちは、高橋真人です。
 さて、Cの基本的なメモリの確保と解放には、malloc()とfree()により行われるとお話ししましたが、Cocoaに使われるObjective-Cでは別にまた専用の仕組みが用意されています。
 Cocoaに限らず、オブジェクト指向のプログラミングでは、オブジェクトと言われるものの存在する場所として、メモリを動的に確保する必要が頻繁に発生しますから、ヒープ領域の利用は“日常的”です。
 Cocoaのコードを少しでも書いたことのある方なら、NSString *aStringとか、NSView *myViewというように、変数の名前の前に必ずアスタリスク(*)が必ず付くことはご存知でしょうし、このアスタリスクがポインタを表すこともCの経験があればお分かりですよね。
 ところで、Cにおいては構造体の変数を宣言する場合、静的に定義することは可能です。

typedef struct {
   char  name[40];
   char  address[120];
   int   age;
} Person;

Person aPerson;

strcpy(aPerson.name, "山田太郎");
strcpy(aPerson.address, "東京都千代田区一番町1-1-1");
aPerson.age = 25;

といったような具合です。
 しかし、Objective-Cのクラスではそれはできません。クラスは形としては構造体の延長上にあるものですから、以下のように静的に宣言しようとすると、

NSString myString;

しっかりコンパイラにチェックされ、

error: statically allocated instance of Objective-C class 'NSString'

というように、許してもらえません。
 つまり、Objective-Cではクラスのインスタンスを宣言する場合には、必ず動的に行う必要があるわけです。

 さて、動的に定義された変数はそのままではオブジェクトを指していませんから、あらかじめ初期値としてnilを代入しておくか、その場で新規にオブジェクトを作成して代入するか、既存のオブジェクトを代入する必要があります。
 これをしない変数に対して何らかのメッセージを送ろうとすると、動作は不定、つまり、アプリはその時点で落ちることになります。
 ただ、nilが代入されている変数についてはこの限りではありません。この変数も「何のオブジェクトも指していない」ことには違いありませんが、これに対してメッセージを送っても、エラーにはなりません。「nilに対して送ったメッセージは単に無視される」ということがObjective-Cの言語として保証されているのです。そのため、変数の宣言をする場合には必ずnilを代入しておく、というのもひとつのやり方ではあります。

 さて、Objective-Cでオブジェクトを作成する場合には、例えば、

NSString *myString = [[NSString alloc] initWithCString:"Hello"];

などといったように、クラスに対してallocメッセージを送り、次いでinitないしそれに類するメッセージを送って初期化をするという流れになるのが普通です。見た目からも分かるように、このallocというメッセージによってヒープ領域からオブジェクトに必要な分量のメモリが確保されるわけです。
 このような、見た目にallocという文字があるものは、用が済んだら使い終わったメモリをきちんとヒープ領域に返さないといけません(ANSI Cのmalloc()とfree()と同様です。)。Objective-Cでは、この場合releaseというメッセージを送ります。
 自分でallocしたオブジェクトに対して使用後にきちんとreleaseを送らないと、メモリリークを招くことになります。

 ところで、オブジェクトを生成するメソッドの中に、allocを使っていないものがあります。例えば以下のようなケース。

NSString *str1 = [[NSString alloc] initWithCString:"Hello"];
NSString *str2 = [str1 stringByAppendingString:@" world!"];

 str1に対して、"Hello"という文字列を入れ、それに、" world!"という文字列を追加したオブジェクトを作って、str2に入れています。間違えないでいただきたいのは、str1とstr2は、それぞれ別のオブジェクトを示しているということです。

 NSLog(@"%@ - %@", str1, str2);

とやってみれば、str1の中は依然として"Hello"のままであることが分かりますし、デバッガなどを使えば、str1とstr2の値が別のアドレス値であることも確認できると思います。
 ここのコードのポイントは、str1に対してstringByAppendingString:というメッセージを送ると、新たに別のオブジェクトが生成されるということです。

 さて、str1、str2ともに使い終わったら、使っていたメモリを解放する必要があります。str1の生成にはallocが使われていますから、当然

[str1 release];

とすればいいのですが、str2についてはどうなんでしょう?

 試しに、str2に対してもreleaseというメッセージを送ってみますと、どうなるでしょうか?
 何と、今度はアプリケーションは落ちてしまいます! 一体どうなっているのでしょう!?
 実は、ここではメモリリークの逆、つまり解放のし過ぎが起こっています。つまり、一度解放したメモリ領域に対して、再度解放のためのメッセージを送ってしまっているのです。ポインタ変数が保持しているアドレス値は、解放によって無効な値と化します。それに対して、再度何かの操作を行うと、プログラムはクラッシュしてしまうのです。

 ここで一つ疑問が湧きませんか?
 str2に対して、releaseを送ってはダメだとすると、このオブジェクトの後始末はいったいどのように行われているのだろうか、と。
 stringByAppendingString:メッセージによって、確かに新しいオブジェクトが作られてstr2に格納されているのに、releaseを呼ばなくてもちゃんとメモリの解放は行われるのでしょうか?

 この仕組みの裏には、「自動解放プール」という仕組みが働いています。次回はその辺をお話ししていきます。

◇MOSAからのお知らせと編集後記は割愛します◇

 

 MOSA Developer News   略称[MOSADeN=モサ伝]
        配信停止 mailto:mosaden-ml@mosa.gr.jp
 記事内容に関するご意見 mailto:mosaden-toukou@mosa.gr.jp
      記事投稿受付 http://www.mosa.gr.jp/?page_id=850
Apple、Mac OSは米国アップル社の登録商標です。またそのほかの各製品名等
はそれぞれ各社の商標ならびに登録商標です。
このメールの再配信、および掲載された記事の無断転載を禁じます。
特定非営利活動法人MOSA  http://www.mosa.gr.jp/
Copyright (C)2009 MOSA. All rights reserved.