MOSA Multi-OS Software Artists

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

プログラマーに興味がある方なら誰でも入会いただけます。
MOSA Multi-OS Software Artists
===SINCE 1995===

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

MOSADenバックナンバー 2008年4月発行分

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

    2008-04-22 

    目次

    • 「「Wonderful Server Life」    第69回   田畑 英和
    • 小池邦人のCarbon視点でCocoa探求
    • ターミナルの向こうから      第24回  海上 忍 

    「Wonderful Server Life」  第69回  田畑 英和

      〜Open Directory編〜

     Open Directoryの解説が続きますが今回はクライアント側のお話です。MacOS XをOpen Directoryのマスターが稼働するMac OS X Serverに接続する方法について解説します。

     まず最初に2つの重要な事を解説します。TigerではOpen Directoryへの接続設定に「ディレクトリアクセス」というユーティリティを使用していましたが、Leopardでは「ディレクトリユーティリティ」という名前に変わりました。インストール場所はこれまでと同じ「/アプリケーション/ユーティリティ」です。「ディレクトリ」というよく似た名前のユーティリティもありますがこちらは別の目的で使用しますので注意してください。
     2つ目ですがLeopard Serverを標準またはワークグループ構成でセットアップし「システム環境設定」でユーザを登録するとネットワークユーザを作成できます。ネットワークユーザはクライアントコンピュータへのログインに使用できるのですが、「システム環境設定」でユーザ登録しただけではクライアントコンピュータへログイン可能なネットワークユーザは作成できません。なぜならばログインシェルがデフォルトでは「/usr/bin/false」に設定されているからです。ネットワークユーザとしてクライアントコンピュータにログインする場合には「ワークグループマネージャ」でログインシェルを変更しておく必要があります。またホームがデフォルトでは「/var/empty」に設定されていますので、別途ネットワークの設定も必要になります。
     いずれにせよシェルやホームが使用できなかったとしても、「システム環境設定」で管理するファイル共有、iCal、iChat、Mail、Web、VPNなどの各サービスは利用可能ですので、必ずシェルやホームを設定しなければならないというわけではありません。実際には使用目的に応じて設定していけばよいでしょう。また標準/ワークグループ構成では、クライアントコンピュータへのログインはクライアント側のローカルユーザを使用することを想定しているといった考え方もできます。
     ネットワークユーザでクライアントコンピュータへログインしないのなら、Open Directoryへの接続など不要ではないかと思う方もいらっしゃると思います。しかし、この後詳しく説明するように接続するとアプリケーションを自動設定したり、ローカルユーザとネットワークユーザを関連付けてローカルユーザでログインした場合でもシングルサインオンを実現することができます。

    ◇Open Directoryへの接続
     では実際の接続方法を解説しましょう。まずは先ほど説明した「ディレクトリユーティリティ」を起動します。場合によってはログイン時あるいは「ディレクトリユーティリティ」を起動した時点で自動的にネットワーク上のOpenDirecotoryを検出して接続設定が始まる場合があります。検証したところサーバが標準もしくはワークグループ構成の場合、Open Directoryのサーバを自動的に検出するようです。
     サーバが自動検出されなくても、画面左下の「+」ボタンをクリックして手動で接続先のサーバを指定することもできます。左下のアイコンがロックされている場合には事前に管理者権限でロックを解除しておきます。
     確実に接続先のサーバを指定して接続したい場合は、自動検出されたサーバは使わずに手動で接続先のサーバを指定してもよいでしょう。

    ・ディレクトリユーティリティ
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#7

     「+」ボタンをクリックした次は、サーバのアドレスを入力します。接続するディレクトリのタイプはデフォルトでOpen Directoryになっていますが、タイプを変更すればActive Directoryに接続することもできます。
     サーバのアドレスを入力すればOpen Directoryのマスターへの接続ができます。

    ・サーバのアドレスを指定
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#8

    ◇クライアントコンピュータの自動設定
     サーバが標準もしくはサーバ構成なら、Open Directoryへ接続したときにクライアントコンピュータを自動設定することもできます。具体的には次のような設定が可能ですが、自動設定をおこなうにはあらかじめサーバ上で該当するサービスが起動している必要があります。

    ・自動設定
      - メールアカウントの設定(IMAPを使用)
      - iChatアカウントの設定
      - CalDAVアカウント(iCalカレンダー)の設定
      - VPNの設定

     自動設定を行う場合にはサーバ上のアカウントを指定し、ログイン中のローカルユーザとの関連付けを行ってから一度ログアウトします。具体的には関連付けるサーバ上のネットワークユーザの名前とパスワードおよびログイン中のユーザのパスワードを入力します。

    ・認証設定(ローカルユーザとネットワークユーザの関連付け)
    http://www.htabata.com/Site/LeopardServer/Pages/DirectoryUtility.html#11

     設定が完了すると、関連付けたネットワークユーザのパスワードがローカルユーザのパスワードと同期され、各種アプリケーション(Mail、iChat、iCal)のアカウントが自動登録されます。また、設定後にログインし直すとシングルサインオンが有効になっています。

     以上Open Directoryへの接続方法について解説しました。次回は自動設定の具体的な内容や、ローカルユーザとネットワークユーザとの関連付けについて解説します。
    次回へつづく                            

    小池邦人のCarbon視点でCocoa探求(2008/04/18)

    〜 NSCodingプロトコルを実装する 〜

    「しんぶんし 3」でドキュメントのロードやセーブを実現するには、ImageFileクラスに対してもう一仕事する必要があることが分かりました。今回は、先んじてこの点を解決してから画像ファイル関連の話へ移りたいと思います。

    おさらいとして、もう一度ImageFileクラスのインスタンス変数を見てみます。スーパークラスとして定義されているNSObjectのさらに右側の表記 が、このクラスがNSCodingプロトコルに準拠していることを示します。

    @interface ImageFile : NSObject   
    {
       NSString        *path;
       NSString        *type;
       CGRect          srt;
       unsigned int    flag;
       int             kind;
       int             para;
    }
    


    NSCodingプロトコルに準拠させるために、ImageFileクラスにencodeWithCoder:とinitWithCoder:メソッドを実装してみます。具体的には、ImageFileオブジェクトからNSDataへ変換する(エンコード)アーカイブ処理と、NSDataからImageFileオブジェクトへ戻す(デコード)アンアーカイブ処理の両方を実現する必要があります。

    ImageFileクラスのインスタンス変数を構造体のメンバーと見なせば、それを一つずつエンコードしていき、6つ全部実行すれば作業は終了となります。以下が、エンコード用に実装するencodeWithCoder:メソッドです。NSMutableArray(list)に保存されているImageFileオブジェクトが順次アーカイブ処理される時に、このメソッドが逐次呼び出されて実行されるわけです。

    - (void)encodeWithCoder:(NSCoder *)coder
    {
       [coder encodeObject:path];
       [coder encodeObject:type];
       [coder encodeValueOfObjCType:@encode(unsigned int) at:&flag];
       [coder encodeValueOfObjCType:@encode(CGRect) at:&srt];
       [coder encodeValueOfObjCType:@encode(int) at:&kind];
       [coder encodeValueOfObjCType:@encode(int) at:&para];
    }
    


    インスタンス変数の最初の2つはNSStringです。既存のCocoaオブジェクトについては、まずはそのクラスが定義されているヘッダファイル(NSString.h)を見てみます。すると、クラス定義の@interface行に対応しているプロトコル名が列記されていることが分かります。

    @interface NSString : NSObject 

    つまり、NSStringの場合には、NSCopying、NSMutableCopying、NSCodingの3つのプロトコルが実装されています。エンコード対象のクラスにNSCodingプロトコルが実装されていれば、NSCoderクラスのencodeObject:メソッドを使えます。これが、対象オブジェクトのencodeWithCoder:メソッドを呼び出すことで、処理が芋ヅル式に進みます。NSStringのpathとtypeに対する処理はこれでOKです。

    続いて、整数(int)、浮動小数点(float)、構造体などの非オブジェクトのエンコードとなります。こちらの実現には、encodeValueOfObjCType:at:メソッドを使います。メッセージとして渡している@encode()は、エンコードされた内容(種類)をC文字列で返すコンパイラ指示子です。例えば、@encode(int)なら”i”に、@encode(flaot)なら “f”となります。構造体の@encode(CGRect)の場合には、”{?=ffff}”(4つの浮動小数点)というC文字列となります。@encodeには、sizeof()演算子の引数として使用できる任意の型を指定できるわけです。@encodeについての詳しい内容については、以下のドキュメントを参照してみてください。

    「Objective-C 2.0プログラミング言語」127ページ

    http://developer.apple.com/jp/documentation/Cocoa/Conceptual/ObjectiveC/

    続いて、ImageFileクラスのデコード用に実装するinitWithCoder:メソッドです。

    - (id)initWithCoder:(NSCoder *)coder
    {
       if( self=[super init] )
       {
           self.path=[coder decodeObject];
           self.type=[coder decodeObject];
           [coder decodeValueOfObjCType:@encode(unsigned int) at:&flag];
           [coder decodeValueOfObjCType:@encode(CGRect) at:&srt];
           [coder decodeValueOfObjCType:@encode(int) at:&kind];
           [coder decodeValueOfObjCType:@encode(int) at:&para];
       }
       return self;
    }
    


    こちらも、NSCodingプロトコル準拠のオブジェクトについては、NSCoderクラスのdecodeObject:メソッドを使います。そうでない整数等はdecodeValueOfObjCType:at:メソッドを使います。この時、デコード順序がエンコード順序と異なると処理は正しく行われません。デコード処理はエンコード時と同じ順序で列記してください。
    ところで、ImageFileクラスのインスタンス変数はプロパティ宣言されています。プロパティの「属性」が(retain)であるpathとtypeは、返り値をself.pathとself.typeに代入しています。これは、コンパイラが用意したアク
    セッサメソッド経由で値を代入したのと同じ意味を持ちます。ここで、返り値をpathやtypeにダイレクトに代入してしまうと、得られたNSStringがautoreleaseされますので、ある時点でエラーが発生してしまいます。注意しましょう。

    また、ImageFileクラスのスーパークラスはNSObjectなので、先んじて以下の様な処理を実行していますが…

     if( self=[super init] )

    もし、このスーパークラスがNSObjectではなくNSCodingプロトコルに準拠した別のクラスであれば、上記の部分は以下のように書き換える必要があります。こちらも注意しましょう。

     

    if( self=[super initWithCoder:coder] )

    さて、これでようやくImageFileオブジェクトのエンコード&デコード処理が実装されました。これにより、オブジェクトデータのドキュメントファイルへの書き出しと読み込み(アーカイブ処理)が無事可能になったわけです。続いて「しんぶんし 3」のドキュメントファイルや画像ファイルの取り扱いを考えてみます。

    ・ドキュメントファイルの場合

    (1)ファイルメニューの「開く…」でファイルを読み込み内容をウィンドウに表示
    (2)ファイルメニューの「保存」で内容をファイルへ書き出す
    (3)ファイルメニューの「別名で保存…」で名称を変えてファイルへ書き出す
    (4)ファイルメニューの「最後に保存した状態に戻す」で編集をキャンセルする
    (5)未保存の編集済みウィンドウを閉じた時には「保存」処理を実行する
    (6)終了時に未保存の編集済みウィンドウに対して「保存」処理を実行する
    (7)アプリ・アイコンへのドロップで起動と内容を表示したウィンドウを開く
    (8)新規や登録済みウィンドウにドロップすることでその内容を追加登録する
    (9)ドキュメントのエイリアスファイルでも上記同等の処理を行えるようにする

    ・画像ファイルの場合

    (1)登録ボタンでウィンドウに画像を追加登録
    (2)ドラッグ&ドロップでウィンドウに画像を追加登録
    (3)アプリ・アイコンへのドロップで新規ウィンドウを開き画像を登録
    (4)画像ファイルが含まれたフォルダについても上記と同等の処理を行う
    (5)画像ファイルのエイリアスファイルについても上記と同等の処理を行う

    ドキュメントファイルの(1)から(7)までの処理は、Info.plistに対象ドキュメントのタイプや拡張子を正しく記載しておけば、NSDocumentクラスが自動で処理してくれますが、(8)や(9)の一部、画像ファイルの(1)から(5)までの処理については自分で実装する必要があるでしょう(多分)。Carbonアプリケーションであれば、こうしたファイル処理は、すべて自分自身で実装(処理用のコードを記述する)することになりますが、「CocoaDocument-based Application」テンプレートを利用すると随分と楽ができます(笑)。

    ただし、Cocoaアプリケーションの場合でも、NSDocumentの力を借りなければ、多くのファイル処理を自分で実装する必要があります。その良いサンプルは、/Developer/Examples/AppKit/に含まれる「TextEdit」です。このサンプルソースコードは、Mac OS X付属アプリ「テキストエディット」のXcodeプロジェクトとなります。Cocoaのドキュメント処理に関してさらに突っ込んで勉強したい方は、ぜひ参照してみてください。

    今回は、ドキュメントや画像ファイルについて実装すべき処理を列挙してみました。ただし、先んじてウィンドウに画像を表示する仕組みを作らないと、こうした処理の動作確認は困難です。次回は、登録された画像ファイルの内容をウィンドウに表示する方法について色々と考察してみたいと思います。
    つづく                                

    ターミナルの向こうから      第24回  海上 忍

    〜  日本語文字コードについて考える (3)〜

     Mac OS Xが支持を集める理由の1つに、”UNIXの資産”を活用できることが挙げられます。スクリプト言語の処理系を見ても、AppleScriptはもちろんPerlにRubyにPythonに……と、有名どころがOS標準で提供されていたりします。今回は、そのうちの1つ「Ruby」を利用し、日本語文字コードを変換してみます。

    ・LeopardにおけるRubyの状況
     本題から始まるのも唐突な印象がありますので、まずは現状認識から。ここ数年でサポートの強化が著しい「Ruby」のいまを、かんたんにまとめてみましょう。
     Leopardでは、Cocoaをはじめとするフレームワークのブリッジ機能を提供する「RubyCocoa」が標準装備されるようになりました。Rubyを利用してMac OSXアプリケーションを開発できることが最大の特徴ですが、コンパイル/リンク不要、対話型のインターフェイス (irb) の装備といった付随機能も充実しています。MOSA伝では、藤本さんの連載「りんご味Ruby」で詳しく紹介されています。
     Leopardリリース後に登場した「MacRuby」も、インパクト大です。MacRubyでは、オブジェクトクラスの親がObjective-CのNSObjectクラス — つまり、すべてのRubyオブジェクトがNSObjectを継承、すなわちCocoaのオブジェクトとなるため、Ruby<->Cocoa間の変換コストが発生しない(Toll-freebridge)うえ、Objective-C 2.0のガベージコレクタも利用できます。Leopardの新機能
    「ブリッジサポート」による恩恵といえますが、あらゆるフレームワークの機能をirbで対話しながら呼び出せることは、多くの開発者にアピールするのではないでしょうか。

    ・Rubyで日本語文字コードを変換する
     Leopardに収録されているRubyのバージョンは、1.8.6です。このバージョンでは、文字コード変換ライブラリとして「Kconv」と「NKF」、「Iconv」の3種類が標準装備されているため、そのうちのいずれかを利用すれば、たいていの日本語文字コード変換処理が可能です。とりあえず、以下では「Kconv」の利用を前提に話を進めます。

     Kconvでは、「Kconv#to*」を利用すると日本語文字コードを変換できます。変換元の文字コードを推定する機能を備えているため、変換先の文字コードを指定するだけでOKです。基本的な使用法は、冒頭でkconvライブラリをロードしておき、「newstring = string.tojis」のように、「to***」の部分に文字コードを示す文字列(jis / euc / sjisなど)を指定します。以下の例では、文字列(ここではシフトJISとします)をUTF-8に変換しています。

    - - - - -
    # -*- encoding: SJIS -*-
    require 'kconv'
    
    origin = "日本語の文字列です"
    origin_utf8 = origin.toutf8
    puts origin_utf8
    - - - - -
    


     注意点としては、いわゆる半角カタカナ(JIS X 0201)を全角カタカナ
    (JIS X 0208)に変換すること、MIME文字列をデコードすることが、デフォルトの動作として設定されている点が挙げられます。以下の例では、ターミナルのデフォルトの文字コードにあわせ、メールヘッダでMIMEエンコードされている文字列をUTF-8に変換していますが、特にオプションは使用していません。

    - - - - -
    $ /usr/bin/irb -Ku
    ≫ moji = "=?ISO-2022-JP?B?TU9TQRskQjt2TDM2SRsoQg==?="
    => "=?ISO-2022-JP?B?TU9TQRskQjt2TDM2SRsoQg==?="
    ≫ moji_utf8 = moji.toutf8
    => "MOSA事務局"
    - - - - -


    ★編集部注:≫は実際には>>です。

     以上の方法では、Kconvによる推測が誤っている場合、「文字化け」が発生します。変換元の文字コードが明らかな場合、変換前後の文字コード(下表参照)を明示したほうが確実でしょう。以下の例では、シフトJISエンコードされた文字列を、UTF-8に変換しています。

    - - - - -
    moji_utf8 = moji.kconv(Kconv::UTF8, Kconv::SJIS)
    - - - - -
    


     Kconv.guessを使用すると、文字列のエンコード形式を推測できます。戻り値は、Kconvの定数となります。以下の例では、ターミナルで入力した日本語文字列(UTF-8)を判定しているので、期待どおり「6」の値が表示されています。

    - - - - -
    $ /usr/bin/irb -Ku
    ≫ moji = " 343 201 275 343 201 222 343 203 240 343 201 237 343 203 223 343 202 262 343 201 252 343 201 277 343 202 207 343 200 234 343 202 223"
    => "ぽげムたビゲなみょ〜ん"
    ≫ Kconv.guess(moji)
    => 6
    - - - - -
    


    - – - – -
    表:Kconvで使用できる定数

    定数  値    文字コード
    JIS    1     ISO-2022-JP
    EUC    2     日本語EUC
    SJIS   3     シフトJIS
    BINARY 4     バイナリ
    ASCII  5     ASCII
    UTF8   6     UTF-8
    UTF16  8     UTF-16
    - – - – -

     RubyCocoaを使用して文字コード変換ツール(もどき)を作成するつもりでしたが、紙幅が足りず……というわけで、次回に続きます。

    ◇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)2007 MOSA. All rights reserved.

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

    2008-04-15

    目次

    • りんご味Ruby         第23回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #136
    • 高橋真人の「プログラミング指南」  第134回
    • 開発ツールよもやま話      MallocDebug後編

    りんご味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回

    プログラマのためのオブジェクト指向再入門(40)

    〜XcodeによるPowerPlant X入門(23)〜

     こんにちは、高橋真人です。早速続きを始めます。
     前回は、PPx::PushButtonのサブクラスを作るための準備として、今までの青いボタンを黒い枠に変更しました。今回はさらに変更を加えていきます。
     それでは、今度は以下の変更を加えてください。
     
    【変更の対象:MyView.hのクラス定義】

    《変更前》
    #include 
    
    class MyView : public PPx::BaseView,
                        public PPx::ControlDrawDoer,
                        public PPx::ControlHitDoer,
                        public PPx::ControlHitTestDoer,
                        public PPx::ControlHiliteChangedDoer
    {
    
    《変更後》
    #include                           // 変更1
    
    class MyView : public PPx::PushButton,              // 変更2
                        public PPx::ControlDrawDoer,
                        public PPx::ControlHitDoer,
                        public PPx::ControlHitTestDoer,
                        public PPx::ControlHiliteChangedDoer
    {
    

    【変更の対象: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からのお知らせと編集後記は割愛します◇

     

     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)2007 MOSA. All rights reserved.

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

    2008-04-08

    目次

    • 「「Wonderful Server Life」    第68回   田畑 英和
    • 小池邦人のCarbon視点でCocoa探求
    • ターミナルの向こうから      第23回  海上 忍 

    「Wonderful Server Life」  第68回  田畑 英和

      〜Open Directory編〜

     今回はサーバ構成が「ワークグループ」の場合のアカウント管理について解説します。基本的には「標準」構成の場合と同様ですが、さらに機能が追加されています。

     アカウント管理の解説を始める前に「ワークグループ」について改めて解説しておきます。「ワークグループ」としてサーバを設定する方法は2通りあります。まず1つ目はLeopard Serverのインストール後にサーバ構成を「ワークグループ」としてセットアップする方法です。この場合セットアップ中にほかのディレクトリサーバへの接続を設定します。
     もう1つは最初は「標準」としてセットアップし、あとからほかのディレクトリサーバに接続する方法です。つまり「標準」と「ワークグループ」の違いは、ほかのディレクトリサーバに接続しているかどうかの違いということになります。ディレクトリサーバへの接続は「ディレクトリユーティリティ」を使用します。
     サーバ構成がどちらになっているかは「サーバ環境設定」の「情報」を表示して「サーバのタイプ」をみれば確認できます。

    ・サーバのタイプ
    http://www.htabata.com/Site/LeopardServer/Pages/Server_Preferences%28Standard%29.html#32

    ◇ワークグループのユーザ管理
     では「ワークグループ」でのアカウント管理についてみていきましょう。まずはユーザからです。「ワークグループ」構成のサーバで「システム環境設定」の「ユーザ」を表示しますと画面がほんの少しだけ「標準」構成とは異なります。ユーザを追加する「+」ボタンが画面左下にありますが、ここにメニューが追加され、「ディレクトリからユーザを読み込む」が選択可能になります。

    ・ワークグループ構成のユーザ管理
    http://www.htabata.com/Site/LeopardServer/Pages/Server_Preferences%28Workgroup%29.html#1

     この追加されたメニューを実行しますと、接続先のディレクトリサーバからユーザを読み込むことができます。読み込みはユーザ単位のほかにグループを指定して読み込むこともできます。グループから読み込むにはギアアイコンのメニューから「グループからユーザを読み込む」を選択します。

    ・グループからの読み込み
    http://www.htabata.com/Site/LeopardServer/Pages/Server_Preferences%28Workgroup%29.html#3

     この読み込み機能ですが、読み込んだからといってオリジナルの(つまり接続先のディレクトリサーバ上の)ユーザアカウントを削除してはいけません。なぜならば、ユーザアカウントの本体はあくまでも接続先のディレクトリサーバ上にあるからです。これは、ユーザアカウントのエイリアスをワークグループサーバ上に作成したと考えると分かりやすいでしょう。ただしたんなるエイリアスではなく、ワークグループサーバ上では読み込んだユーザアカウントに属性を追加することができるのです。このようにして、オリジナルのアカウントにはなにも修正を加えずに、ユーザ属性を管理することができるようになるのです。

     このようなユーザの読み込みはLeopard Serverの新規で、読み込んだユーザのことをAugmented Usersと呼んでいます。ディレクトリサーバ上のアカウントに直接属性を追加する場合、スキーマの拡張が必要であったり、そもそも属性の追加ができなかったりする場合がありますが、Augmented Usersを利用することにより、より柔軟なユーザ管理が可能になります。
     特にLeopard Serverになってからユーザ属性に様々な情報を追加するようになってきましたので、ユーザ属性を自由に管理できることは重要になります。

    ◇ワークグループのグループ管理
     「ワークグループ」ではグループ管理にも機能が追加されます。「システム環境設定」の「グループ」を表示すると「外部メンバー」ボタンが追加されていることが分かります。この外部メンバーでは接続先のディレクトリサーバ上に存在するアカウント(ユーザもしくはメンバー)をワークグループサーバ上のグループのメンバーとして追加することができます。
     異なるディレクトリ上のアカウントをグループのメンバーにすることは、すでにTiger Serverから可能でしたが、「システム環境設定」から容易に外部メンバーを追加できるようになりました。この機能を利用することで異なるディレクトリ上のアカウントを1つのグループとして管理できるようになります。

    ・外部メンバー
    http://www.htabata.com/Site/LeopardServer/Pages/Server_Preferences%28Workgroup%29.html#8

     以上が「ワークグループ」構成で利用可能なアカウント管理機能になります。ほかのディレクトリサーバとの連携が「サーバ環境設定」のシンプルなGUIで実現できるといったところがポイントになります。それでは次回は「ディレクトリユーティリティ」を使用した、ディレクトリサーバへの接続について解説する予定です。
    次回へつづく                             

    小池邦人のCarbon視点でCocoa探求(2008/04/04)

    〜 プロパティとプロトコル 〜

    今回からは、画像ファイルを読み込んで、そのパス名などをImageFileオブジェクトへと登録する仕組みをどうするのか順次考えて行きます。その前に、せっかくObjective-C 2.0を使うわけですから、以前に作成したImageFileクラスのインスタンス変数を「プロパティ」仕様に準拠させてみます。

    インスタンス変数をプロパティ仕様に準拠させれば、自動的にコンパイル側でそれへのアクセッサメソッドを作成してくれます。ですから、アクセッサメソッドを別途ソースコードとして記述する必要はなくなります。また、C言語での構造体メンバーへのアクセスと同じように、「ドット演算子」を用いて、インスタンス変数へのアクセスが可能となります。例えば、こんな感じのソースコードが記述可能になるわけです。

    ImageFile    *image;
    
    if( image=[[ImageFile alloc]init] )
    {
      image.name=@"Untitled";
      image.type=@"JPEG File";
      image.kind=1;
      image.flag=0;
    }
    


    ドット演算子経由での変数アクセスを考慮し(今のままでは見づらいので)、今回からインスタンス変数の先頭のアンダーラインは外すことにしました。この「先頭アンダーライン」ですが、荻原剛志さんの著書「Objective-C」には、先頭にアンダーラインを付けたインスタンス変数は避けるべきだと記載されています。Apple社が、Framework内のクラスなどで使用することはOKだが、一般開発者は使用しない方が良いと言う説もあります。ところが、Apple社のサンプルソースコードを見ると、何のためらいもなく使用している場合もあり(笑)Cocoa開発者を悩ませます。さて、何が正解なのでしょうか?

    ともあれ、ImageFileクラスで用意したインスタンス変数をプロパティ仕様に準拠させるには、クラスの雛形宣言(ImageFile.h)の中で@property宣言を使いインスタンス変数を列挙します。

    @interface ImageFile : NSObject 
    {
       NSString        *path;
       NSString        *type;
       CGRect          srt;
       unsigned int    flag;
       int             kind;
       int             para;
    }
    
    @property(retain)NSString      *path;
    @property(retain)NSString      *type;
    @property(assign)CGRect         srt;
    @property(assign)unsigned int   flag;
    @property(assign)int            kind;
    @property(assign)int            para;
    
    @end
    


    @propertyに続くカッコ内の(retain)は、プロパティの「属性」と呼ばれており、retain、assign、copy、readonly、readwriteなど幾つかの種類があります。それらの詳しい機能については、以下のドキュメント(日本語訳)を参照してみてください。

    「Objective-C 2.0プログラミング言語 」
    http://developer.apple.com/jp/documentation/Cocoa/Conceptual/ObjectiveC/

    例えば、属性がそれぞれretain、assign、copyであると、コンパイラが作成するアクセッサメソッドのタイプが次のように変わります。

    // assignの場合
    
    - (void)setKind:(int)newKind
    {
       kind=newKind;
    }
    
    // retainの場合
    
    - (void)setPath:(int)newPath
    {
       if( path!=newPath )
       {
           [path release];
           path=[newPath retain];
       }
    }
    
    // copyの場合
    
    - (void)setPath:(int)newPath
    {
       if( path!=newPath )
       {
           [path release];
           path=[newPath copy];
       }
    }
    


    オブジェクトでないインスタンス変数(intやfloatや構造体など)であれば、assign属性でOKだと言うのが理解できます。今回は、NSStringの場合にだけretain属性を使います。そして、もう一カ所だけプロパティに関係する定義を記述しておくべき箇所があります。それはクラスの実装部分(ImageFile.m)の方です。対象となるインスタンス変数について@synthesize宣言しておきます。実際の定義は、以下のようになります。

    @implementation ImageFile
    
    @synthesize path,type,srt,flag,kind,para;
    
    // 各種メソッド表記
    
    @end
    


    プロパティ宣言についてはこれで完了です。ところで、ImageFile.hの@interface行の最後を見てみると、そこにという記述がありますが、これは、ImageFileクラスがNSCodingプロトコルに準拠していることを意味しています。プロトコルとは、幾つかのメソッド(ひとつでも良い)のグループを指しており、そのメソッドをすべて実装したクラスのみが「そのプロトコルに準拠している」と呼ばれます。NSCodingプロトコルはNSObject.hに定義されており、以下の2つのメソッドを持つクラスがそれに準拠していることになります。当然、あるだけではなく、それらのメソッドがプロトコル定義の意図した動作をしないといけません。

    @protocol NSCoding
    
    - (void)encodeWithCoder:(NSCoder *)aCoder;
    - (id)initWithCoder:(NSCoder *)aDecoder;
    
    @end
    


    つまり、ImageFileクラスに、上記2つのメソッドを実装する必要があるわけです。これらのメソッドはどういう働き(機能)をするのでしょうか? 前回、NSDocumentメソッドをオーバライドして、以下の2つのメソッドを実装しました。dataOfType:error:メソッドは、ファイルにドキュメントを書き出すために、オブジェクトをバイナリデータ(NSData)に変換します。また、readFromData:ofType:error:メソッドは、ファイルから読み込んだバイナリーデータ(NSData)からオブジェクト(NSMutableArray)を再構築します。

    - (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError
    
    - (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName
                                           error:(NSError **)outError
    


    両メソッドの実際の処理に注目してみましょう。NSMutableArrayをバイナリー
    データ(NSData)に変換しているのは、NSArchiverクラスのarchivedDataWithRootObject:メソッドです。逆にバイナリーデータ(NSData)をNSMutableArrayに戻しているのは、NSUnarchiverクラスのunarchiveObjectWithData:メソッドです(こちらでもインスタンス変数の_listは先頭のアンダーラインを取りました)。この2つのアーカイブとアンアーカイブメソッドは、対象となるオブジェクトのクラスがNSCodingプロトコルに準拠していることが前提で使用されています。

    data=[NSArchiver archivedDataWithRootObject:list] ) // バイナリーデータ作成

    if( array=[NSUnarchiver unarchiveObjectWithData:data] ) // データからNSArrayを得る
        [list addObjectsFromArray:array]; // インスタンス変数に保存する

    NSArray.hを見ると、NSMutableArray(NSArrayのサブクラス)は、NSCodingプロトコルに準拠していることが分かります。

    @interface NSArray : NSObject                                                            NSFastEnumeration>

    よって、NSMutableArrayに対しては両メソッドを使用しても大丈夫ですが、その配列に代入されているImageFileオブジェクトの方は、NSCodingプロトコルに準拠しないと問題が生じます。つまり、ImageFileクラスにencodeWithCoder:とinitWithCoder:メソッドを実装する必要があり、具体的には、ImageFileオブジェクトからNSDataへ変換するアーカイブ処理と、NSDataからImageFileオブジェクトへ戻すアンアーカイブ処理の両方を実現しておくことになります。

    本アプリケーションでドキュメントのロードやセーブを実現するには、ImageFileクラスに対してもう一仕事する必要があることが分かりました。次回では、先んじてこの点を解決してから画像ファイル関連の話へ移りたいと思います。
    つづく
                                    

    ターミナルの向こうから      第23回  海上 忍

    〜  日本語文字コードについて考える (2)〜

     ありがたいことに、世の中にはさまざまな文字コード変換ツールがフリーで配布されています。前回は文字コード変換用のコマンドを紹介しましたが、今回はPerlモジュールの形で実装された文字コード変換用の関数 / コンポーネントの使い方を紹介します。

    ・Perlで日本語文字コード変換
     Mac OS X 10.5(Leopard)には、Perl 5.8.8が標準装備されています。シェル(ターミナル)またはAutomatorから呼び出すスクリプトの形で利用できるため、広範囲に活用できます。
     Perlには、日本語環境用に文字コードの変換を行う拡張モジュールが数種類存在します。ここでは、CGIでの利用も考慮して、Perl 5.8以前の環境でも利用できる「Jcode.pm」と、Perl 5.8以降に標準装備されている「Encode.pm」の2つのモジュールの使い方を紹介します。

    i) Jcode.pm
     日本語文字コード変換を行うためのPerlモジュールです。バージョン5.8のPerl以降では、Jcode.pmが持つすべての機能は、新たに標準のモジュールとして採用された「Encode.pm」に引き継がれているため、積極的に選択する必要はありませんが、なんらかの理由で旧バージョンのPerlを使用する場合には有用です。なお、Jcode.pmのインストール方法は、第21回をご参照ください。
     Jcode.pmの機能は、Perlスクリプトの先頭部分に「use Jcode;」の1行を挿入し、「Jcode::convert($str, $out-code, $in-code, “z”);」の要領で使用します。もっとも、ソースとなる文字列から新しくオブジェクトを生成する、すなわち「$result = Jcode->new($source, “utf8″)->sjis;」の要領で記述するほうがスマートでしょう。

    - - - - -
    リスト1:Jcode.pmで文字コードを変換(EUC→S-JIS)する
    
    use Jcode;
    $result = Jcode->new($source, "euc")->sjis;
    - - - - -
    


     入力する文字列の符号化形式を、自動判定させる方法もあります。その場合には、入力の符号化形式を省略するだけでOKです。たとえば以下の例では、対応する文字コード種(ISO-2022-JP=”jis”、シフトJIS=”sjis”、日本語EUC=”euc”、UCS-2=”ucs2″、UTF-8=”utf8″)で符号化された日本語文字列を、シフトJISとして変数(result)に代入しています。

    - - - - -
    リスト2:符号化形式を自動判定してシフトJISに変換する
    
    $result = Jcode->new($source)->sjis;
    - - - - -

     余談ですが、工夫すれば文字コード変換用のコマンドとしても活用できます。入力する文字列(ファイル)の符号化形式は、前述のとおり省略すると自動判定されるので、かなり簡略化することが可能です。たとえば、以下のコマンド実行例では、オリジナルを拡張子「.bak」付きのファイルとしてバックアップしつつ、目的のテキストファイル(source.txt)の符号化形式をUTF-8に変換しています。AutomatorやAppleScriptで文字コード変換だけを目的に使うのであれば、この応用で十分対応可能でしょう。

    - - - - -
    コマンド実行例:
    
    $ perl -MJcode -i.bak -lpe 'jcode( $_)->utf8' source.txt
    - - - - -
    


    ii) Encode.pm
     Leopardに付属のPerlには、Jcode.pmの後継モジュール「Encode.pm」が標準装備されています。改良点としては、Jcode.pmでEUC-JPからUTF-8に変換すると、チルダ「~」が文字化けしてしまう(unicode::japanese、http://search.cpan.org/dist/Unicode-Japanese/ を使用するなど回避法あり)など、Unicodeの扱いに生じていた問題が解消されたことが挙げられます。
    Encode.pmの場合、標準の状態でUnicodeに対応しています。処理速度も、Jcode.pmをかなり上回ります。
     Encode.pmの機能は、Perlスクリプトの先頭部分に「use Encode;」の1行を挿入すれば利用できます。日本語EUCからシフトJISに変換することを例にすると、オーソドックスな書き方では「from_to($str, ‘euc-jp’,'shiftjis’);」、オブジェクト指向的に書けば「$data = encode(‘shiftjis’,decode(‘euc-jp’, $str));」といったところです。

    - - - - -
    リスト3:Encode.pmで文字コードを変換(EUC→S-JIS)する
    
    use Encode;
    $data = encode('shiftjis', decode('euc-jp', $str));
    - - - - -
    


     ただし、符号化形式の自動判定機能を利用するには、Encode::Guessモジュールが必要です。その際、事前に候補となる文字コードを列挙しておかなければならず、デコードに成功したものが複数だった場合、成功した方式の文字列を返すという仕様があるため、ごく短い文字列の変換が思いどおりに行かないことがあります(試しに、以下のサンプルで$dataを「あいうえお」としてください)。簡便さを優先するならば、速度を犠牲にしてもJcode.pmを使うほうがいいでしょう。

    - - - - -
    リスト4:Encode::Guessを利用した文字コードの自動判定
    
    use Encode;
    use Encode::Guess qw/shift-jis euc-jp 7bit-jis/;
    $data = "ここに日本語の文字列を入力します";
    $data = encode('shiftjis', decode('Guess', $data));
    print $data;
    - - - - -
    

    ◇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)2007 MOSA. All rights reserved.

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

    2008-04-01

    目次

    • りんご味Ruby         第22回  藤本 尚邦
    • 藤本裕之のプログラミング夜話   #135
    • 高橋真人の「プログラミング指南」  第133回
    • 開発ツールよもやま話      MallocDebug前編

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

    ウェブや電子メールで英文などの外国語を読むとき、あるいは英作文しなくちゃならないときなど、英語が苦手な私はYahoo!翻訳などのウェブサイトをよく利用します。あらかじめ用意されているウェブのインターフェースをそのまま使うのもいいのですが、もっと手早く手軽に使えるようにしたいということがありますよね。ネットで探してみれば何かそのためのお助けツールが見つかるかもしれませんが、自分が使いやすいように作ることもできます。私の場合は、RubyでYahoo!翻訳のテキスト翻訳を使うためのライブラリを作って、これを元にシェルコマンド・Emacsコマンド・Quicksilverプラグインを作って使っています。今回から数回、このライブラリをどんな風に作るか・使うかを紹介していきます。

    ■ テキスト翻訳ライブラリの使い方(仕様)

    テキスト翻訳の機能は、「何語から何語に翻訳したいのか?」および「翻訳対象テキスト」を入力に「翻訳結果テキスト」を出力する関数として見立てることができます。

    Yahoo!翻訳によるテキスト翻訳器をYahooHonyakuという名前のクラスとして定義することにして、翻訳を実行する上記の関数をテキスト翻訳器のただ一つの公開されたインスタンスメソッドとして実装することにします。テキスト翻訳器(YahooHonyakuのインスタンス)を生成(new)し、メソッドを呼び出して翻訳を実行するといった使い方が基本になります:

     translator = YahooHonyaku.new
     translator.translate(:je, "おはよう")     => "Good morning"
     translator.translate(:ej, "Good morning") => "おはよう"
    


    1つめの引数が「何語から何語に翻訳したいのか?」で、2つめの引数が「翻訳対象テキスト」を表します。「何語から何語に翻訳したいのか?」は、それぞれ対象言語、結果言語を表す2文字のシンボル(または文字列)で表します。:ej なら「英語から日本語に訳」、:jeならその逆といったようになります。

    実際にコードを書いたりデバッグなどで試してみるときには、翻訳器を生成して翻訳するところまでを一度にやってしまいたいことも多くなりそうです。それは翻訳器クラスのクラスメソッドとして用意しておくことにしましょう:

     YahooHonyaku.translate(:je, "おはよう")     => "Good morning"
     YahooHonyaku.translate(:ej, "Good morning") => "おはよう"
    

    ■ 翻訳器クラスの概要

    Yahoo!翻訳による翻訳器クラスの定義の概略は以下のようになります。

    ------------------------------------------------ yahoo-honyaku.rb
    class YahooHonyaku
     def self.translate(mode, text)
       new.translate(mode, text)
     end
    
     def translate(mode, text)
       post!(:eid=>_eid_(mode), :text=>text)
       @yt_result
     end
    
     private
    
     def _eid_(mode)
       raise NotImplementedError
       # 変換モードをYahoo!翻訳の実引数の値に正規化する
     end
    
     def post!(form_args={})
       raise NotImplementedError
       # Yahoo!翻訳に実引数を渡して呼び出し
       # 結果をパースして
       # 翻訳結果を @yt_result にセットする
     end
    end
    ------------------------------------------------

    翻訳を実行するインスタンスメソッド translate から呼ばれる post!では、HTTPのPOSTメソッドを介して、テキスト翻訳を実行して、HTMLとして返された結果をパースして、翻訳されたテキストを取り出します。この中身は次回以降で実装します。

    ■ クラスメソッドと特異メソッド

    Rubyでは、1つ1つのオブジェクトに個別にメソッドを定義することができます。そのようなメソッドを特異メソッドといい、以下のように定義します:

     def オブジェクト.hoge(...)
       ...
     end
    


    あまり意味のない例ですが:

     obj = Object.new
     obj.hoge         # NoMethodError発生
    
     def obj.hoge
       puts "hoge hoge"
     end
     obj.hoge         # hoge hoge と印字
     Object.new.hoge  # NoMethodError発生
    


    この hogeというメソッドは、objと名付けられたオブジェクトだけに実装されます。YahooHonyakuに戻ると、翻訳器の生成と翻訳を一度にまとめて実行するクラスメソッドは:

     def self.translate(mode, text)
       new.translate(mode, text)
     end
    


    のところで定義しています。このselfはYahooHonyakuクラス自体を表しています。つまりこのメソッド定義は、self(すなわちYahooHonyakuクラス自体)の特異メソッドを定義していることになります。Rubyでは、クラス自体も数値や文字列などと同等に扱える値(ファーストクラスオブジェクト)です。Rubyでは、クラス自体に定義された特異メソッドのことを、便宜上クラスメソッドと呼んでいるだけなのです。

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

     前回最後で私はちょっとおかしなことを書いた……いや書いたつもりだったんだけど、誰からもありゃどういう意味だと聞かれたりしなかったのは、私が思ってることが実は常識なのかそれとも誰もこんな文章読んでないのか。念のために再録しておく。こう書いたのね……。『俺たちが作っているこいつは実のところ「ソフトウエアの名に値しない何か」なんぢゃないかと思っている』。ほんぢゃなんだってんだ? と誰が突っ込んでくれると思ったんだけどね(笑)。

     WebのYahoo辞書の「大辞泉」で「ソフトウエア」というのを検索してみると以下のような結果が表示される。

    1. 機器類を用いて行う物事の、情報・理論など無形の部分
    2. コンピュータの、処理の手順を示すプログラムの総称

    そしてこの両方に、反対語として「ハードウエア」と書いてあるのね。これに対して、e-Wordsが提供しているIT用語辞典だと当然のように「ソフトウエア」の意味は上の2の方だけになる。曰く……

    狭義にはコンピュータプログラムとほぼ同じ意味(中略)。広義にはコンピュータが扱うプログラム以外のデータを含めてソフトウェアと呼ぶ場合もある。

     確かに我々が作っているのは「コンピュータプログラム」なんだけどさ。
    「大辞泉」がまず挙げているように、その昔「コンピュータ」がMTをカタカタ回しつつ怪獣の弱点を紙テープで教えてくれるものであり、「プログラム」と言えばそれは「式次第」や「演目表」のことであり、「IT」と言ったらスティーブン・キングの恐怖小説(これは知ってるひとだけか)しか思い浮かばなかった……新石器時代中葉ごろ(嘘)、「ソフトウエア」というのは、「テレビ受像機やビデオデッキに対して映し出される映像(あえて「映像作品」とは言わないが)」のことであり、楽器やレコードに対して演奏される楽曲」のことであり、書籍や雑誌といった媒体に対してそこに印刷される「文章」のこ
    とであった、のである。

     今は知らず、他人は知らねど、少なくとも私自身とその周囲には、我々が作るコピュータプログラムもそうした広義での「ソフトウエア」の範疇に入るものであり、すなわちこれは「新しく生まれた『ソフトウエア』のありかた」であるという考え方があった。いやホントですって。

     そういう考え方がわりと一般的になりつつあった証拠に、私の手元には10年ほど前に作ったソフトウエアに関する契約書がある。その第1項は甲(このソフトの販売会社)が乙(ワタシのこと)にこのCD-ROMの「企画印税を支払う」というもので、1枚あたりの企画印税は税抜き価格より容器代を控除した金額の1%、容器代は税込み価格の10%となっている。第2項にリクープ(recoup)の取り決めがあって、「本件企画印税は上記CD-ROMの工場出荷枚数7,001枚より発生するものとする」……つまり7,000枚までの売り上げに関する印税報酬は、このソフトの製作費として貰ったお金と相殺で、7,000枚を超えるヒットとなったら1枚につき……計算すると18円くらいになったのかな? が支払わ
    れるというわけである。え? 1%は安すぎないかって? このソフトは私が基本のコードを書き、それをウインドウズ版に移植した人がいて(ハイブリッドだった)、その他にもデータを作った人、音楽を載せた人などもろもろが印税を分け合うので1%なのである。別に不当な契約ではない。

     それに結論からいうと、このソフト、それなりに話題にはなったものの7,000枚を超えては売れず、私には製作費以外一銭も入ってこなかったので、この1%が別に0.1%でも逆に2%でもその後の世界は何も変わらなかったのである。が、しかしここで言いたいのはそういう愚痴ではなくて(笑)、コンピュータソフトウエアがあんな風に他の「ソフトウエア」と同様なものとして扱われるのは果たして適当だったのか、ということなんである。当時は「いいこと」と思っていたんだけどね。
                           (以下次回 2008_03_29)

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

    プログラマのためのオブジェクト指向再入門(39)

    〜XcodeによるPowerPlant X入門(22)〜

     こんにちは、高橋真人です。
     さて、BaseViewを拡張してボタンの仕掛けを組み込む要領が分かったところで、これからはいよいよPPxならではのメリットを生かした拡張をしていきます。
     Carbonフレームワークの一部であるHIToolboxの提供するHIViewには、サブクラスを作る仕組みが備わっています。しかし、HIToolboxはCベースのAPIであるため、サブクラスを作れると言っても、オブジェクト指向言語でやるほどスムーズではありません。
     慣れてしまえばそんなに難しくもないのかもしれませんが、それなりにコードを書く必要もあり、少し煩雑な印象があります。それに対して、PPxの場合にはC++というオブジェクト指向に対応した言語を使うため、ずっとスムーズに書くことができます。
     もっとも、PPxでも内部的にはHIViewのCのAPIを使って実装を行っているわけですから、やれることにはそんなに違いはありません。でも、サブクラスされたViewが完全に独立したオブジェクトとして振る舞うので、コードのすっきり感はずっと上がるのではないかと個人的には思っています。
     サブクラスを作れることの大きなメリットは、“既存の仕組みを流用”し、足りないところは補い、変更したい部分は書き替える(オーバーライド)ことができることです。ですから、HIViewでサブクラスを作れるという意味は、もともとシステムが備えている様々な種類のView、つまり、ボタンとかMLTE(簡易エディタ機能を持ったテキストエリア)などの仕組みをそのまま流用しつつ、さらに自分なりに新たな仕組みを加えられるということです。
     しかし、私が以前探してみた範囲では、Appleの提供しているサンプルはすべてがHIView(最もベーシックなView。それだけでは何も機能は持たないので、もともとサブクラス化して使うようになっているもの)からの継承した例でした。
     でも、もし本当にHIViewからだけしかサブクラスを作れないのだとすれば、HIViewの掲げる「サブクラスを作れる」という看板には偽りあり、ですよね。
     そんなわけで、本当にダメなのかをちょっと試してみました。
     結果は全く問題なしでした。たくさんテストしたわけではありませんが、シンプルなボタンのサブクラスを作ることも難なくできました。

     そんなわけで、これからはこれを実際にやっていきます。もちろんPPxでです。ただ、PPxの拡張の柔軟性をお伝えしたいので、多少のバリエーションを付けながら、複数のアプローチでやってみたいと思っています。とは言え字数の都合上、余り凝ったことはできませんので、やること自体はシンプルですが。もっとも、その方が理解するためには好都合かもしれませんね。
     さて、最初にやるのはごく普通のボタンに装飾を加えることです。PPxにはPushButtonというシステム標準のボタン(HIViewではkHIPushButtonClassIDと表します)のためのクラスがありますが、これのサブクラスを作って装飾(というほど立派なものではありませんが・笑)を施してみます。
     では、早速やってみましょう。
     例によって一つ前のプロジェクトを複製して、PPxForXcode07を用意してください。(もう、細かい説明は必要ありませんよね?)
     今回は、コードを一挙に掲載するのではなく、段階的に変更を加えていきます。
     まずは準備段階として、前回作ったView(MyView)の大きさをボタンの大きさに合わせます。そのために以下の変更を加えます。

    【変更の対象:MyApplication.cpのDoSpecificCommand()】

    《変更前》
       HIRect frame = CGRectOffset(contentFrame,
                                   20.0, contentFrame.size.height - 60.0);
       frame.size = CGSizeMake(120.0, 40.0);
    
    《変更後》
       HIRect frame = CGRectMake(
                        contentFrame.origin.x + 20.0,
                        contentFrame.origin.y + contentFrame.size.height - 60.0,
                        120.0,
                        20.0);
    


     前回は、ちょっと変な書き方をしたのですが、今回は素直にしました。これで、“普通の”ボタンのサイズでViewを作るようになります。
     あと、もう一つ。今度はMyViewの方の準備のための変更です。

    【変更の対象:MyView.cpのDoControlDraw()】

    《変更前》
       float alpha = 0.4;
    
       if (::IsControlHilited(inControl) and ::IsControlActive(inControl)) {
           alpha = 0.8;
       }
    
       ::CGContextSetRGBFillColor(inContext, 0.3, 0.6, 1.0, alpha);
       ::CGContextFillRect(inContext, frame);
    
    《変更後》
       ::CGContextStrokeRect(inContext, frame);
    


     では、ここで一度ビルドして走らせてみましょう。
     表示されるのは、とりあえず黒い線で書かれた枠だけですが、クリックしてみるとちゃんとボタンとして機能していることが分かります。今はビジュアル的には反応はしませんが。
     次回はさらに変更を加えていきます。

    開発ツールよもやま話     MallocDebug 前編   高橋 政明

     MallocDebugはパフォーマンスツールのひとつでメモリ関連の分析ツールです。Xcode Toolsをインストールすると/Developer/Applications/Performance Tools/フォルダにインストールされます。最新バージョンは1.7です。

    Cocoa Fundamentals GuideのMallocDebugの説明を引用すると
     プログラムに現在割り当てられているすべてのメモリブロックを割り当て時の呼び出しスタック別に整理して表示します。一目見ただけで、アプリケーションがどの程度の割り当てメモリを消費しているか、そのメモリがどこから割り当てられているか、どの関数に大量のメモリが割り当てられているか分かります。MallocDebugでは、プログラムのほかのどこでも参照されていない割り当てメモリを見つけることができるため、リークを見つけたり、メモリが割り当てられた場所を正確に追跡したりするのに役立ちます。
            【Cocoaとは? 開発環境 その他の開発ツールより引用】

     水漏れ(リーク)と虫眼鏡のアイコンからも直感的に分かるように、最も活躍するのはメモリリークを見つける時でしょう。
     MallocDebugは分析対象のアプリケーションと同時に走らせてダイナミックにメモリの確保や解放処理をどこから呼ばれたかと同時に記録するツールです。それを分析しメモリリークなどを表示してくれます。Helpメニューからリリースノートを見るとMac OS XのPublic Betaの時にバージョン1.0だったことがわかります。MallocDebugの使い方をマスターすればどのバージョンのMacOS Xでも使えるわけです。

     MallocDebugでアプリケーションを分析するために何らかの特別なライブラリをリンクしたり、特別な関数を呼び出したりする必要はありません。

    ◆概要
     画面や操作を文字だけで説明するのはかなり辛いのでAppleのサイトを引用しながら説明します。Memory Usage Performance Guidelines の Examining
    Memory Allocation PatternsにMallocDebugの説明が載っています。
    http://developer.apple.com/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingPatterns.html
     このページのFigure 1にMallocDebugの画面の説明があります。
     一番上がLaunch informationで分析対象のアプリケーションを指定し必要なら起動時の引数を設定します。(Finderからダブルクリックで起動するアプリの場合引数の指定は不要)
     その下はMallocDebugの操作用ポップアップボタンと「Mark」「Update」ボタンがあります。
     その下のCall Stack Browserはメモリ確保を行った関数の呼び出し階層と確保した容量などを表示する部分です。
     一番下のMemory Buffer Listが確保されているメモリのアドレスやサイズなどの一覧です。ここをダブルクリックすると対応するメモリの内容を16進数表示で確認する事ができます。

     確保したメモリはMallocDebugにより0×55で埋められ(初期化)ています。また確保したブロックは0xdeadbeef と 0xbeefdead (この部分文字ではなく16進です)で挟まれています。このことを念頭に16進数表示のMemory ViewerPanelの内容を見る事で自分のプログラムのどの部分が処理したかや、意図通り動作しているかを追求することができます。

    ◆基本操作
     まず分析するアプリケーションを指定します。Browseボタンで選ぶこともできますし、Finderからコンボボックスへアプリケーションアイコンをドラッグ&ドロップも可能です。過去に分析したアプリケーションのフルパスをコンボボックスから選ぶこともできます。
     Launchボタンで指定したアプリケーションを起動します。

     Call Stack Browserにはmallocを呼び出した関数が階層表示されます。フレームワークや様々なAPIが確保するメモリもすべて記録表示します。

     表示モードはDisplay Modeポップアップボタンで変更できます。デフォルトのStandard modeはmainから関数の呼び出し階層を表示します。(Figure 3参
    照)
     Inverted modeはStandard modeの逆に表示します。
     Flat modeはメソッドまたは関数ごとに確保したメモリ順にソートして表示します。

     Analysis Modeについてですが、All Allocationsは現時点のすべてのメモリを表示します。
     Allocations since MarkはMarkボタンを押した後の操作などで観測対象のアプリケーションが新たに確保したメモリだけを表示します。注目部分だけを観測できます。
     Leaksはメモリリークを表示します。詳しくは次回説明する予定です。
     Overruns/underrunsはバッファの直前または直後への不正な書込みを表示します。直後へオーバーした場合は「>」マーク、直前へ書き込んだ場合は「<」マークをMemory Buffer Listに表示します。
    (後半へ続く)                            

    ◇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)2007 MOSA. All rights reserved.