プロパティリスト(Property List)は永續化(シリアライズ)の爲の手段である。Windows ユーザでも iTunes ライブラリを展開したものは、ここを覗く人なら居るかも知れない。嚴密にはプログラミング言語ではないが、Cocoa ではあまりにも重要なので扱ふことにする。
以上の物とそのサブクラスが對稱となる。以外はプロパティリストとして保存できない。またコレクションクラス(配列と辭書)の場合は格納するオブジェクトもこの基準に沿はねばならない。
Cocoa では Foundation フレームワークに plist 專用のクラスやメソッドが用意されてゐる。NSPropertyListSerialization クラスである。
次の三つのクラスメソッドが定義される。
### NSPropertyListSerialization Class: Tasks # Serializing a Property List + dataFromPropertyList:format:errorDescription: # Deserializing a Property List + propertyListFromData:mutabilityOption:format:errorDescription: # Validating a Property List + propertyList:isValidForFormat:
NSData 形式でテキストを渡しオブジェクトを復元して返すのが propertyListFromData メソッドである。 propertyList メソッドは復元可能かを調べる爲に使用する。dataFromPropertyList はオブジェクトインスタンスを永續化したテキストを NSData に格納して返す。エンコードは XML ならば UTF-8 になる。
+ (id)propertyListFromData: (NSData *)data mutabilityOption:(NSPropertyListMutabilityOptions)opt format:(NSPropertyListFormat *)format errorDescription:(NSString **)errorString + (BOOL)propertyList:(id)plist isValidForFormat:(NSPropertyListFormat)format + (NSData *)dataFromPropertyList:(id)plist format:(NSPropertyListFormat)format errorDescription:(NSString **)errorString
引數はこのやうになる。NSPropertyListFormat と NSPropertyListMutabilityOptions は以下の定數をとる。
NSPropertyListMutabilityOptions: NSPropertyListImmutable NSPropertyListMutableContainers NSPropertyListMutableContainersAndLeaves NSPropertyListFormat: NSPropertyListOpenStepFormat # ASCII Property List NSPropertyListXMLFormat_v1_0 # XML Property List NSPropertyListBinaryFormat_v1_0 # Binary Property List
NSPropertyListFormat は種類の指定であり、NSPropertyListFormat は復元方法である。NSPropertyListFormat を引數にとる propertyList メソッドはどの種類に適合するかを判斷する訣である。dataFromPropertyList は通知された種類に永續化を試み、propertyListFromData は通知された種類に解する。
propertyListFromData で指定する NSPropertyListMutabilityOptions は變更可能なオブジェクトとするか否かであり、通常は NSPropertyListImmutable である。NSPropertyListMutableContainers は變更可能なオブジェクトであるが、その内部構成までは變更可能ではないインスタンスとして返す。NSPropertyListMutableContainersAndLeaves は全てのインスタンスが變更可能なオブジェクトとなる。
これらには特別に次のメソッドが與へられてゐる。
### NSArray Class: Tasks # Creating an Array + arrayWithContentsOfFile + arrayWithContentsOfURL # Initializing an Array - initWithContentsOfFile - initWithContentsOfURL # Creating a Description - writeToFile - writeToURL ### NSDictionary Class: Tasks # Creating a Dictionary + dictionaryWithContentsOfFile + dictionaryWithContentsOfURL # Initializing an NSDictionary Instance - initWithContentsOfFile - initWithContentsOfURL # Storing Dictionaries - writeToFile - writeToURL ### NSString Class: Tasks Converting String Contents Into a Property List – propertyList – propertyListFromStringsFileFormat
NSArray 及び NSDictionary には復元と保存の兩樣のインスタンス乃至クラスメソッドがある。
NSData や NSString の場合 writeTo*** は中身を出力する爲のメソッドであり、NSString に關しては出力するファイルエンコーディングを指定して變換し保存する物だが、NSArray と NSDictionary は plist として保存する爲のメソッドになる。
NSString の propertyList は自身を Property List として解析しインスタンスを生成しそれを返し、propertyListFromStringsFileFormat は strings形式(cf. ASCII形式)として自身を解析して NSDictionary を返す。
Mac OS X v10.5 から讀み書きができるやうになります。
この形式の利點は人がみて讀みやすい所にある。ただし、古い形式であるため NSDate は NSString と、眞僞値は數値との區別がつかなくなる。
クラス | 値 | 記法 | 備考 |
NSString | string | "string" | 値が英數字及び「_」のみの時はダブルクオート無し。一部エスケープされる |
NSNumber | YES(bool) | 1 | ---- |
NSNumber | NO(bool) | 0 | ---- |
NSNumber | 20(int) | 20 | ---- |
NSNumber | 2.0(float) | 2.0 | ---- |
NSDate | @referencetime | "YYYY-MM-DD HH:mm:ss ±XXXX" | ISO に基づくが、これは恐らく description の値 |
NSData | 0xAD238FE8321..... | <10101101 00100011 0011.....> | バイナリは二進數に變換され、8bit=1byte ごとに半角スペースが入る |
NSArray | [obj,obj,.....,obj] | (<obj>, <obj>, ...., <obj>) | <obj>の記法は各々のクラス依存 |
NSDictionary | [key=>obj,key=>obj] | {<key>=<obj>; <key>=<obj>; } | <obj>の記法は各々のクラス依存で、<key>はNSStringに同じ |
Unicode のバイナリ値がそのまま出力される。やうは UTF-16 で出力する。「あ」は2ビットコードであり「U+3042」である。これは「\U3042」と出力される。改行コードは LF は「\n」に置換されるが、CR はそのままである。タブは「\t」に変換され、ダブルクオートもまた「\"」となる。バックスラッシュは重ねることになる。嚴密には、2ビット以上のUnicodeであるとバイナリ値へエスケープされるらしい。
文字 | エスケープ | 備考 |
a | a | 値が英數字及び「_」のみの時はダブルクオートが無く、また左記に同じ。ASCIIの他は次のやうになる |
あ | \U3042 | Unicode は U+3042 |
タブ | \t | ---- |
改行(LF) | \n | ---- |
改行・復歸(CR) | (そのまま) | ---- |
ダブルクオート(") | \" | ---- |
バックスラッシュ(\) | \\ | ---- |
コレクションはその文字數を一列に並べた時に78バイトを超過すれば、アイテムごとに改行することになる。
{ "Application Version" = "8.0.1"; Features = 5; "Library Persistent ID" = XXXXXXXXXXXXXXXX; "Major Version" = 1; "Minor Version" = 1; "Music Folder" = "/Users/XXX/Music/iTunes/iTunes%20Music/"; Playlists = ( { ..... }, ....... ); }
セミコロン(;)及びコロン(,)は必ず半角スペースを一つ後置する。これはダブルクオートで括らない場合への對處。コレクションの要素がこれらで區切られぬ場合、パーサはエラーを發する。なほ NSArray の最後の項目にはコロンが無いのが標準だが、うつかり附けてもきつちり認識される。
コメントは次の三つの形式を許可する。
お馴染みであらう。前者二項は改行、後者一項は「/*」で始まり「*/」で終はるまでがコメントと看做される。
strings は Localizable.strings など、NSBundle クラス等で取得しローカライズ辭書で使用される、UTF-16 でエンコーディングされた書類である。これのためにパーサは NSDictionary がルートである時に限り、ルート要素の NSDictionary には鍵括弧を必要としない。
よつて先程示した ASCII の plist は次のやうでも同樣に解釋されることになる。
"Application Version" = "8.0.1"; Features = 5; "Library Persistent ID" = XXXXXXXXXXXXXXXX; "Major Version" = 1; "Minor Version" = 1; "Music Folder" = "/Users/XXX/Music/iTunes/iTunes%20Music/"; Playlists = ( { ..... }, /* これは註釋 */ ....... ); # これも註釋 // これもさうである
Localized.strings では普通使はないけれど、他の NSArray などのコレクションをこれは要素として持つ事ができる。
あまりこれといつた面白みがない。まづ手始めに DTD があるのでそれをみてみよう。/System/Library/DTDs/PropertyList-1.0.dtd にインストールされてゐる。http://www.apple.com/DTDs/PropertyList-1.0.dtd からも參照できる。
<!ENTITY % plistObject "(array | data | date | dict | real | integer | string | true | false )" > <!ELEMENT plist %plistObject;> <!ATTLIST plist version CDATA "1.0" > <!-- Collections --> <!ELEMENT array (%plistObject;)*> <!ELEMENT dict (key, %plistObject;)*> <!ELEMENT key (#PCDATA)> <!--- Primitive types --> <!ELEMENT string (#PCDATA)> <!ELEMENT data (#PCDATA)> <!-- Contents interpreted as Base-64 encoded --> <!ELEMENT date (#PCDATA)> <!-- Contents should conform to a subset of ISO 8601 (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with a loss of precision) --> <!-- Numerical primitives --> <!ELEMENT true EMPTY> <!-- Boolean constant true --> <!ELEMENT false EMPTY> <!-- Boolean constant false --> <!ELEMENT real (#PCDATA)> <!-- Contents should represent a floating point number matching ("+" | "-")? d+ ("."d*)? ("E" ("+" | "-") d+)? where d is a digit 0-9. --> <!ELEMENT integer (#PCDATA)> <!-- Contents should represent a (possibly signed) integer number in base 10 -->
この DTD の素晴らしい點はこれをみれば Foundation の六つのクラスに必要なことと役割がある程度わかることである。
エレメント | 對應クラス | 値 | 屬性 | 備考 |
plist | ---- | エンティティ plistObject を一つ | version | 屬性は version="1.0" のみ許容 |
array | NSArray | エンティティ plistObject を0個以上 | ---- | ---- |
dict | NSArray | エレメント key とその次にエンティティ plistObject 、この組を0個以上 | ---- | ---- |
key | ---- | 文字列 | ---- | 強いて言へば對應クラスは NSString |
string | NSString | 文字列 | ---- | ---- |
data | NSData | 文字列 | ---- | ただし書きには Base64 encode濟 であるとある |
date | NSDate | 文字列 | ---- | ISO 8601 に基づく |
true | NSNumber | ---- | ---- | 値を取てはならない |
false | NSNumber | ---- | ---- | 値を取てはならない |
real | NSNumber | 文字列 | ---- | ただし書き正規表現に從ふ文字列 |
integer | NSNumber | 文字列 | ---- | 10進數のみ |
エンティティ plistObject は plist タグと key タグを除く全てになる。普通はしない DTD を定義したのは可視性の向上などにあると思はれる。この單純な(けれど冗長な)コーディングによつて、プログラマは xml を開いてみただけで何がシリアライズされてゐるのか把握することができるのである。
NSData のバイナリデータを Base64 でシリアライズするといふことは、それなりに厖大な長さになることが容易に想像できるかと思ふ。68文字(68バイト)で折疊むのが標準の模樣
ASCII 形式ほどに編輯頻度は高くない。XML 形式のコメントが當然使へる。
<?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>Application Version</key><string>8.0.1</string> <key>Library Persistent ID</key><string>XXXXXXXXXXXXXXXX</string> <key>Major Version</key><integer>1</integer> <key>Minor Version</key><integer>1</integer> <key>Music Folder</key><string>/Users/XXX/Music/iTunes/iTunes%20Music/</string> <key>Playlists</key> <array> <dict> ........ </dict> <dict> . . </dict> </array> </dict> </plist>
ASCII の Property List と同じ内容とした。dict、array はこのやうに改行することが多いが、data も68文字を越えて改行するとこのやうになる。
<array> <data> AQEAAw................ ... ... AAAAAAAAAAAA== <data> <array>
一部調査中。「bplist00」で始まるバイナリファイル。パフォーマンス向上の爲に考案された。
追記:opensource.apple で公開されてる CF(CoreFoundation) Projectに CFBinaryPList.c といふファイルを發見。なんでもつと早く Carbon を思ひ出さなかつたんだらう・・・。
// CFBinaryPList.c /* HEADER magic number ("bplist") file format version OBJECT TABLE variable-sized objects Object Formats (marker byte followed by additional info in some cases) null 0000 0000 bool 0000 1000 // false bool 0000 1001 // true fill 0000 1111 // fill byte int 0001 nnnn ... // # of bytes is 2^nnnn, big-endian bytes real 0010 nnnn ... // # of bytes is 2^nnnn, big-endian bytes date 0011 0011 ... // 8 byte float follows, big-endian bytes data 0100 nnnn [int] ... // nnnn is number of bytes unless 1111 then int count follows, followed by bytes string 0101 nnnn [int] ... // ASCII string, nnnn is # of chars, else 1111 then int count, then bytes string 0110 nnnn [int] ... // Unicode string, nnnn is # of chars, else 1111 then int count, then big-endian 2-byte uint16_t 0111 xxxx // unused uid 1000 nnnn ... // nnnn+1 is # of bytes 1001 xxxx // unused array 1010 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows 1011 xxxx // unused set 1100 nnnn [int] objref* // nnnn is count, unless '1111', then int count follows dict 1101 nnnn [int] keyref* objref* // nnnn is count, unless '1111', then int count follows 1110 xxxx // unused 1111 xxxx // unused OFFSET TABLE list of ints, byte size of which is given in trailer -- these are the byte offsets into the file -- number of these is in the trailer TRAILER byte size of offset ints in offset table byte size of object refs in arrays and dicts number of offsets in offset table (also is number of objects) element # in offset table which is top level object offset table offset */
書き出すと長くなりさうだし、PropertyList Editor で書き出したファイルと上の註釋とを照らし合はせてほしい。
そんなものはない。素人に編輯はできない。