MOSA Multi-OS Software Artists

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

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

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

2009-04-28

目次

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

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

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

まず、前回までに完成した、CGContextクラスとCGPDFContextクラスの根幹部分のプログラムを確認しておきましょう(本文末尾の cg_context.rb を参照のこと)。この cg_context.rb を使ってPDF描画のプログラムを書くと以下のようになります:

 

url  = OSX::NSURL.fileURLWithPath("circle.pdf")
 rect = OSX::NSRect.new(0, 0, 612, 792)
 context_ref = CGPDFContextCreateWithURL(url, rect, nil)

 pdf = CGPDFContext.new(context_ref)
 pdf.beginPage(nil)
   pdf.setRGBFillColor(1.0, 0.0, 0.0, 1.0)
   pdf.addArc(300, 300, 100, 0, 2 * Math::PI, 1)
   pdf.fillPath
 pdf.endPage
 pdf.flush
 # CFRelease(context_ref)  # (注)


ぱっと見たところ、後半部分にDSL化した効果は見られるものの、これだけであれば、わざわざ DSL にするほどのメリットがあるのかどうか微妙な感じです。さらに DSL的要素を追加していくことにしましょう。

(注: 今作っているDSLを、変更せずそのまま MacRuby(0.4) で動かすことはできませんが、もし現時点(2009.4.23)の MacRuby 0.4 向けに書き直す場合には、本文のPDF描画プログラム例の最後のところで、CFRelease を使ってcontext_ref で使われたリソースを明示的に解放する必要があります)

■ ブロック構文の導入

beginPage と endPage は、間に挟んだ CGContext 向け関数の呼び出しをページと関連づけるための対になっています:

 pdf.beginPage
   ....    # あるページに対する描画
 pdf.endPage


beginPage と endPage は、組み合わせて使うことによって初めて意味を持ちます。CGContext向けの関数の中には、このように組み合わせて使うことを想定した関数の対がいくつかあります。

そもそも、CoreGraphicsに限らずとも、C言語向けのライブラリやAPIには、このような関数の組み合わせがよくありますね。Cの標準ライブラリに入っているfopen と fclose がその代表といったところでしょうか。古代からのMacプログラマの方であれば、HLock と HUnlock とか GetPort と SetPort の組み合わせを思い浮かべる方もいらっしゃるかもしれません。

このタイプのAPIを使ったCプログラミングでは、対の片方(たいがいは後の方)を書き忘れるというのはありがちなバグです。では何故、バグの原因になりやすいこのような関数の組み合わせがC言語用のライブラリやAPIには存在するのでしょうか? それは、C言語には無名の手続きをデータとして扱う手段がなく、1つの安全なAPIとして提供する簡単な手段がなかったから、ということもあるでしょう(名前のついた関数を引数にとるAPIとして作ることはできますが、使い勝手はあまりよくなさそうです)。

Rubyには、無名手続きをデータとして扱う手段の1つとして、ブロック構文があります。ブロック構文を使って、API関数の対で挟まれる手続きの部分をデータとして扱うことにより、2つのAPI関数が必ず対で呼ばれることを保証する1つのAPIを簡単に作ることができます。

では、beginPage と endPage を組み合わせて呼び出す1つのDSL構文 page を作り、対の片方を書き忘れるというバグの原因を根元から取り去ってしまいましょう:

 

class CGPDFContext
   def page(page_info={}, &block)
     beginPage(page_info)
     block.call(self)
   ensure
     endPage     # このメソッドから出るときに必ず呼ばれる
   end
 end


ensureは、ensure と end の間の文式が、スコープ(この場合メソッド全体)の本体から出るときに必ず呼ばれることを保証するRubyの構文です。Javaのfinally、Objective-Cの@finallyに相当します。

仮引数のblockが、対に挟まれた手続き(あるページに対する描画)になります。beginPageしたあと、block.callにより描画手続きが実行され、その後endPageすることになります。前述の描画プログラム例の後半部分を、page を使って書き直すと以下のようになります:

 

pdf = CGPDFContext.new(context_ref)
 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
 pdf.flush


beginPage と endPage の対ではなく page を使うことにより、beginPageが呼び出されたら、それに対応するendPage が必ず呼び出されるようになります。endPage は書き忘れようがありません。(次回に続きます)

■ CGContextクラスとCGPDFContextクラスの根幹部分

-------------------------- cg_context.rb (未完成) --
# -*- coding: utf-8 -*-
require 'osx/cocoa'

class CGContext
 # context_type 宣言構文の定義:
 #   コンテキストの種類(PDFなど)を宣言するとCG(種類)Context関数のイン
 #   スタンスメソッドを生成する
 class << self
   private
   def methodnize(original_name, name)
     method_name = name[0].chr.downcase + name[1..-1]
     define_method(method_name) do |*args|
       OSX.__send__(original_name, @context, *args)
     end
   end
   def context_type(type)
     pattern = / ACG#{type}Context(.*) Z/
     OSX.methods.each do |func_name|
       matched = pattern.match(func_name)
       methodnize(func_name, matched[1]) if matched
     end
   end
 end

 # デフォルトの context_type 宣言
 context_type ''

 # CGContextクラスのインスタンスオブジェクトの初期化
 def initialize(context)
   @context = context
 end
end

class CGPDFContext < CGContext
 context_type :PDF
end

class CGBitmapContext < CGContext
 context_type :Bitmap
end
---

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

 さて、コマかいことを言い出すと、そもそもニホンにおけるインターネットのコト始めは1984年、東京大学、東京工業大学、慶應義塾大学が実験的にネットワークを組んだ JUNET が云々……てな話になるわけだが(またそう書かないと納得しないヒトも多いのだが)、ここでは「インターネット」という言葉が流行語大賞のトップ10に選ばれた1995年をとりあえず「インターネットが一般化した年」として話を進める。

 あ、せっかく調べたので書いておくと、この年の流行語大賞は3つあって「無党派」(受賞者は故・青島幸男)と「NOMO」(同じく野茂英雄)、そして「がんばろうKOBE」(同じく故・仰木彬)である。なんだか懐かしいよねぇ。

 閑話休題。論じたいのはこの「インターネットの普及」とゆーものが、コンピュータ・ソフトウェア業界、特にソフトウェア開発者の状況にどんなインパクトを与えたか、ということである。

 え?「つまりはそれまでのプラットフォームに加えて Web開発というものが加わったとか言いたいのか」って? チッチ、そんな大学の、しかもソフトウェア工学ではなく情報処理史みたいな与太授業を受け持っているセンセイがいいそうなことは言いませんよ。ベクトルが違う。

 つまりね、「インターネットの普及」は、コンピュータ……なかでもパーソナル・コンピュータをものすごい勢いで「フツウのヒトが使うもの」に変えたわけ。

 たとえが適切かどうかわからんが、かつて皇太子(今の天皇)ご成婚や東京オリンピックが一般家庭へカラーテレビを「押し込んだ」ように、「インターネット」という言葉がパソコンの販売台数を劇的に押し上げたのである。ま、ほとんど「Windows95」マシンだったけどさ。

 その結果なにが起きたか。

 ヒトツには「パソコンを純粋に『使う』だけのユーザーが劇的に増加」した。もっとはっきり言えば「コンピュータのことを何も知らず、また勉強するつもりもないユーザーが増えた」のである。

 それから「インターネット」がビジネスの媒体として機能するようになり「コンピュータに関わる仕事」のバリエーションが広がった。それまでは「コンピュータ関係の仕事」といえば,まぁ十中八九はエンジニア(開発者)かセールスマン(営業)だったのが、「サイトプランナー」だとか「ハイパーメディアクリエータ」だとか「スベッタコロンダコンサルタント」だとかそういうちょっと聞いただけではオレでも「具体的になにをやってんだかまったくわからない」肩書きのヒトがやたら増えた。

 で、こっからがオレの問題意識のマトなのだけど、この2つ、つまり「ユーザーがシロウトばっかりになり、クロウトの種類が増えた」ことの相乗効果として、「ソフトウェア技術者」という概念がとっても曖昧なものになってしまったんだよね。

 それまでだって、厳密ということを尊ぶ我々は「SE」と自称する連中が時として「セールス・エンジニア」(なんだそりゃ? と思いました)だったり、「システム・アーキテクト」とか名乗るオトコが実現可能性も考えずにただ大風呂敷を広げるだけのクチサキ小僧だったりすることに苛立ってはいた。が、この時オレ達の足下にクチを開けた地獄はもう一段上……いや下か、とにかく
ちょっとトンデモナイものだった。

 何を言いたいかわからない? つまりですね、「ツールを使ってWebサイトの体裁を良くする仕事をするだけのヒト」も「高度で専門的な知識や経験を必要とするソフトウェア技術者」ナノだよ、この地獄では。
(以下次回 2009_04_25)                       

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

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

 皆さんこんにちは、高橋真人です。
 さて、今回からObjective-Cのメモリ管理のお話をします。Cocoaのフレームワークが持つ高度なメモリ管理の仕組みを見る前に、まずはObjective-Cの一部でもある、Cのメモリ管理について見てみましょう。
 Cには以下の3種類のの変数があります。

・ローカル変数(局所変数または自動変数とも呼ばれる)
・グローバル変数(大域変数または外部変数とも呼ばれる)
・static変数(静的変数とも呼ばれる)

 この中で、ローカル変数は自動変数とも呼ばれるように、メモリの管理が自動的に行われます。それ以外の変数はプログラムの終了時までずっとメモリを保持し続けるので、特にメモリ管理を考える必要はありません。
 このように、Cが元々用意している仕組みを利用するだけならば、メモリ管理ということについていちいち気にする必要はありません。しかし、プログラムに柔軟性を生むために「メモリの動的な確保」ということを行うのが、現実的なCでのプログラミングです。
 この動的なメモリ確保のために使われるのが、ANSI Cライブラリのmalloc()という関数です。この関数を呼び出すと、通常ローカル変数のために利用するスタック領域とは別のヒープ領域というメモリのエリアから、引数に指定されたサイズのメモリを連続的に確保して、その先頭のアドレス値を返してくれます。
 malloc()で確保したメモリは、明示的にfree()という関数を呼び出さない限り解放されることはありませんから、変数のスコープなどとは関係なく確保したままにすることができます。これは逆から見れば、free()を使ってきちんと後始末をしないと、そのメモリ領域は他では使えない状態のままに置かれるこということでもあります。
 もっともヒープ領域と呼ばれる領域も、システムによって割り当てられている、そのプログラムのための専用のメモリ領域の一部ではありますので、プログラム自体が終了すれば自動的にシステムに返されます。
 なので、少しの量のメモリであれば、いちいち後始末など気にしなくても、という考え方があるのも事実です。ただ、それを言い出すとメモリ管理の話から外れてしまうので、今はそれは考えないことにします。

 さて、malloc()で確保したメモリはfree()で解放すると言いましたが、動的に確保されるメモリ領域は1つに限るわけではありません。なので、それぞれを識別するための情報が必要になるわけです。そこでmalloc()が返したアドレス値をポインタと呼ばれる「メモリのアドレスを格納する型」の変数に保管しておき、この値をfree()に渡すことで、解放すべきメモリ領域を伝えます。
 このように、プログラム側から見ると、動的に確保したメモリが不要になった際、きちんとそれを解放するためには、malloc()が返したアドレス値を保持しておくことが必要となります。例えば以下のようなコードを書くとします。

void foo(size_t memory_size)
{
   char *ptr = malloc(memory_size);
   if (ptr == NULL) {
       fprintf(stderr, "Memory allocation failed.\n");
   }
   else {
       fprintf(stderr, "Memory allocation suceeded.\n");
   }
}


 malloc()が返した値は、ちゃんとptrという名のポインタ変数に受け取ってはいるものの、この関数が終わったとたんにptrという変数自体は消滅してしまいます。しかしながら、free()はこのメモリ領域に対して呼ばれていませんから、領域自体は確保されたままになっています。
 この関数から抜けた後は、メモリ領域を識別するための情報は失われてしまっていますから、この領域を解放することはもはや不可能です。こういう状態を「メモリリーク」と呼び、確実なメモリ管理のためには避けなければならないこととされています。

◇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.