plistの各要素は、Foundationと呼ばれるCocoaのフレームワークに存在するクラスと強く結びついている。plistの要素に対応するクラスが存在する上、plistの内容を格納したNSStringという文字列クラスに対しpropertyListというたった1つのメソッドを送るだけでplistの内容がパースされ、それぞれのクラスを用いたオブジェクトが生成、メモリに展開される。
リスト4はその簡単なサンプルだ。NSStringクラスのインスタンスであるfilecontentには、filenameで指定されるファイルの内容がテキストとして丸ごと格納される(リスト4-1)。このfilecontentに対してpropertyListメッセージを送る(リスト4-2)と、その内容がパースされ、メモリ上に同等の構造をしたオブジェクト群がロードされる。
実行例2はリスト4の実行結果で、ここではリスト2のplistを読み込ませている。NSLog()関数*の出力からplistで表現されたのと同等のオブジェクトが生成されているのが分かる(図1)。
% ./plistread list2.plist
2005-10-10 18:39:49.454 plistread[1282] {
"\U30ad\U30fc1" = "\U50241";
"\U30ad\U30fc2" = (
"\U914d\U5217\U30e1\U30f3\U30d0\U30fc1",
"\U914d\U5217\U30e1\U30f3\U30d0\U30fc2",
"\U914d\U5217\U30e1\U30f3\U30d0\U30fc3"
);
}
propertyListメソッドの実行に失敗した場合、例外が発生する(実行例3)。Objective-Cにおいて例外をとらえるには、NS_DURINGからNS_HANDLERで囲っておくと良い(リスト4-3)。例外発生時はNS_HANDLERからNS_ENDHANDLERまでの内容が実行される。また、例外の内容を示すオブジェクトはNS_HANDLERからNS_ENDHANLDERまでの間で有効な変数localExceptionに格納されている。このlocalExceptionの内容を確認することで、どこでパースエラーが起こっているのかが分かる*。
% ./plistread list2_wrong.plist
2005-10-10 19:33:30.682 plistread[1745] in thread <NSThread: 0x502e60>\
{num = 1, threadDictionary = (null)},exception caused.
name: <NSParseErrorException>
reason: <XML parser error:
Close tag on line 18 does not match open tag dict
Old-style plist parser error:
Malformed data byte group at line 1; invalid hex
>
userInfo: <(null)>
ファイルへの出力を行うには、NSArrayもしくはNSDictionaryに対して「writeToFile:atomically:」メッセージを送ると良い。リスト5はそのサンプルだ。
リスト5の「@""」はソースコード中に埋めることのできるNSStringのリテラル*である。NSDictionaryは、生成時の構造からその内容を変更できないのに対し、そのサブクラスであるNSMutableDictionaryはその内容を変更可能という違いがある。ここでは、空のNSMutableDictionaryクラスのインスタンスを生成し、そこに順番にオブジェクトを格納していった後ファイル名を第1引数、真偽値のYES*を第2引数に「writeToFile:atomically:」メッセージを送付している。リスト6がその結果だ。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Key1</key>
<string>Value1</string>
<key>Key2</key>
<dict>
<key>SubKey1</key>
<string>Value2</string>
</dict>
<key>Key3</key>
<array>
<string>Member1</string>
<string>Member2</string>
<string>Member3</string>
</array>
</dict>
</plist>
このように、読み出しおよび書き込みに関しては簡単に処理される。Cocoaでは、データの格納、操作のために利用するNSStringやNSDictionaryといったオブジェクトは日常的に使われているため、わずか1つのメッセージを送るだけでファイルに保存し、オブジェクトを手軽に永続化できるのだ。
Foundationと同様に基本的なデータ構造を扱うフレームワークである「CoreFoundation」の紹介を交えつつ、「Mac OS Xの『魂』」であるFoundationとplistの理解を深めていきたい。
NSLog()関数は、標準エラー出力に対して、第1引数で指定された書式で文字列を出力する手軽なデバッグ用の関数であり、「%@」という書式でObjective-Cのあらゆるオブジェクトを文字列に変換、出力を行う。なお、このとき出力先での文字化けを防ぐため、ASCII外の文字に関しては「\Uユニコード値」にエンコードされる。
読み込むだけなら、NSDictionaryの「dictionaryWithContentsOfFile:」、ないしはNSArrayクラスの「arrayWithContentsOfFile:」メソッドを使えば1行で可能だが、これらはplistの文法が間違っていたとき単純にnilを返すだけで、エラーチェックが難しいという弱点がある。
GCCに含まれるObjective-Cコンパイラは「@""」からなる文字列をNSStringクラスのサブクラスである静的文字列クラスに展開する。この静的文字列クラスは、NeXTおよびGNUstepの古いバージョンではNXConstantStringクラスで、Mac OS XではNSConstantStringクラスになる。なお、「@""」の中にはASCII内の文字しか含めることができない。
歴史的な事情から、YESは単に1の、NOは0に対するマクロであり、C++のtrue、falseのような真偽値ではない。
本記事は、オープンソースマガジン2006年1月号「Undocumented Mac OS X」を再構成したものです。
Copyright © ITmedia, Inc. All Rights Reserved.