環境設定とデフォルトデータベース【実践的Macintoshプログラミング解説】

印刷用表示 |テキストサイズ 小 |中 |大 |

CoreData版 Repeating Motif Generator の開発 Repeating Motif Wonderland CoreData 実践的 Macintosh プログラミング解説

LinkIconホーム

更新日 2009-05-24

原図のパラメータをランダムに決める

環境設定とデフォルトデータベース

 原図は六個の発振器出力を使って作られます。各発振器はそれぞれ三つのパラメータを持つので、原図は18個のパラメータを持つ事になります。18個のパラメータを常に手動で設定しなくてはならないというのは面倒なので、乱数を使ってパラメータを設定する機能を追加します。

 乱数でパラメータを決めるといっても全くのランダムではなく、ある範囲内でパラメータを変化させる様にします。その変化させる範囲を環境設定でユーザに指定してもらう事にします。このような場合、システムが用意しているデフォルトデータベースというものを使いますので、まずこれについて説明します。

ランダマイザの機能

 ランダマイザの機能は原図のパラメータを乱数を使って決定する事です。ただし振幅と周波数は各発振器ごとに変化する範囲を指定できるものとします。これは周波数の変化範囲に制限を付けたかった、という理由でつけている機能です。

 やってみるとわかりますが、周波数を制約なしにランダムに決定すると、高い周波数が現れて原図が単なるぐちゃぐちゃの線の固まりになる確率が高いのです。これだと面白みがありません。例えばこんな感じの原図です。

dullMotif.png

 発振器ごとに制約をつける事で、ランダムでありながらある程度の方向性を決める事ができます。例えば第一の発振器は振幅を大きめ、周波数を低めにして全体の形を大まかに決める役割、第三の発振器は周波数を高めにするが振幅を小さめにしておいて波形にちょっとしたアクセントを付ける役割、第二の発振器はその中間といった具合です。

環境設定とデフォルトデータベース

 振幅と周波数を変化させる範囲の指定は、環境設定パネルを作成してそこで行う様にします。環境設定パネルにスライダーをいくつか並べて、各発振器ごとにどこからどこまでの範囲でランダムに変化させるのかを指定できる様にします。こんな感じです。

preference.png

preferenceBinding.png ユーザに指定してもらった値はシステムが用意しているデフォルトデータベースに保存します。デフォルトデータベースへ保存する事はInterface Builder上の設定だけで実現でき、プログラミングは不要です。

 例えば左上のスライダーは右の様に設定すればOKです。Bind to:の対象としてShared User Defaults Controllerを選択すると、自動的にコントローラが作成されてnibウィンドウに現れます。

 モデルキーパスは値を保存する際に使う名前を自分で決めて入力します。ここで入力した名前は、後でランダマイザで使います。


 保存はこのように簡単なのですが、保存した値を読み取るには若干のコードが必要です。ランダマイザは起動時にバインディングを設定する事により、インスタンス変数とデフォルトデータベースの値を同期させます。

 デフォルトデータベースを使用するにあたって必要な設定がありますので、説明します。

デフォルトデータベースを使用するにあたって必要な設定

 デフォルトデータベースを使用するにあたって必要な設定があります。識別子を設定する事と、アプリケーションデフォルトを設定する事です。アプリケーションデフォルトは項目が多いので、プロパティリストの形で用意しますが、それについても説明します。

識別子の設定

 デフォルトデータベースを使用するにあたって必要な設定があります。識別子の設定です。

 マニュアルを、ADC Home > Reference Library > Guides > Mac OS X > Runtime Configuration Guidelines >とたどっていくとCFBundleIdentifierの説明にたどり着きます。CFBundleIdentifierが識別子にあたります。

CFBundleIdentifier

This key identifies the type of the bundle. This identifier should be a uniform type identifier (UTI) string, for example com.mycompany.MyApp. This key does not uniquely identify a specific bundle in the file system, as multiple copies of an application with the same or different version may exist. See Uniform Type Identifiers Overview for details on UTIs.

The preferences system uses this string to identify the application for which a given preference applies. Launch Services uses the bundle identifier to locate an application capable of opening a particular file, using the first application it finds with the given identifier.

 厳密な訳ではありませんが、まあ大体こんな事が書いてあります。

このキーでバンドルのタイプを識別します。この識別子は例えばcom.mycompany.MyAppの様にUTI文字列であるべきです。このキーはファイルシステムの中で特定のバンドルを一意的に識別する訳ではありません、というのは同じバージョンあるいは異なるバージョンのアプリケーションが複数存在するかもしれないからです。UTIの詳細についてはUniform Type Identifiers Overviewを見て下さい。

環境設定システムはこの文字列を、与えられた環境設定があるアプリケーションに適合するかどうかの識別に使います。ラウンチサービスはこの識別子を、特定のファイルを開く能力があるアプリケーションを見つけるのに使います。

 要するにデフォルトデータベースを使うときは、どのアプリケーションが書き込んだのかを識別するためのIDを設定しないといけないという事です。この設定はXcode上で行う事ができます。(Info.plistを直接編集してCFBundleIdentifierを書き換えても結構です)

 この設定はターゲットを設定するで既に済ませているので、そちらを参照して下さい。プロパティ設定の識別子の項目です。

デフォルト値の設定

 アプリケーションを最初に起動したときは、ユーザーが設定した値(ユーザーデフォルト)というものは当然ながらありません。そのときに読み出される値が不定値では困りますから、デフォルトの値(アプリケーションデフォルト)を設定する必要があります。この事は"User Defaults Programming Topics for Cocoa"というドキュメントの"Using NSUserDefaults - Setting a Default in the NSRegistrationDomain"に書いてあります。

To register the application’s default behavior, you get the application's shared instance of NSUserDefaults and register default values with it. A good place to do this is in the initialize method of the class that uses the default. The following example registers the value “YES” for the default named “DeleteBackup”.

+ (void)initialize{

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSDictionary *appDefaults = [NSDictionary
        dictionaryWithObject:@"YES" forKey:@"DeleteBackup"];

    [defaults registerDefaults:appDefaults];
}


The initialize message is sent to each class before it receives any other message, ensuring that the application's defaults are set before the application needs to read them.

 これを読むと

  • アプリケーションデフォルトの値を設定するにはNSUserDefaultsクラスのregisterDefaults:メソッドを使う
  • registerDefaults:の引数はNSDictionaryで、辞書にはキーと値のペアを予め書き込んでおく。
  • アプリケーションデフォルト値設定を実行するよい場所は、デフォルトデータベースを使用するクラスのinitializeメソッドである。initializeというメッセージは他のメッセージを受け取る前に各クラスに送られるので、アプリケーションが値を読む前にアプリケーションデフォルトが確実にセットされる。

という事がわかります。デフォルトデータベースを使用するクラスはランダマイザなのですが、initializeメソッドはAppControllerに既に実装しているので、ここに追加する事にします。

 またサンプルでは辞書の中身をソースコードで設定していますが、数が多いとこれでは大変です。NSDictionaryにはdictionaryWithContentsOfFile:というクラスメソッドがあって、ファイルを読んで辞書を生成する事ができますので、これを使います。

 辞書の中身を記述したUserDefaults.plistというファイルをResourcesフォルダの中に作ります。NSBundleクラスのpathForResource:ofType:メソッドを使うと、このファイルのパスを得る事ができますので、得られたパスをdictionaryWithContentsOfFile:に渡してやればOKです。したがって、initializeメソッドの実装は以下の様になります。

//
//  AppController.m
//

#import "AppController.h"

#import "RMGPathToImageTransformer.h"


@implementation AppController

#pragma mark -
#pragma mark Initializer

+ (void)initialize
{
    RMGPathToImageTransformer   *pathToImage;
    NSString                    *udPath;
    NSDictionary                *udDict;
    
    //  ValueTransformerを登録する
    pathToImage = [[[RMGPathToImageTransformer alloc] init] autorelease];
    [NSValueTransformer setValueTransformer:pathToImage
                                    forName:@"RMGPathToImage"];
    
    //  ユーザデフォルトの初期値を登録する
    udPath = [[NSBundle mainBundle] pathForResource:@"UserDefaults" 
                                             ofType:@"plist"];
    udDict = [NSDictionary dictionaryWithContentsOfFile:udPath];
    [[NSUserDefaults standardUserDefaults] registerDefaults:udDict];
}

UserDefaults.plistを作る

 NSDictionaryのdictionaryWithContentsOfFile:で読めるファイルはプロパティリスト形式のXMLファイルで、ルートが辞書になっている必要があります。中身もプロパティリストオブジェクト(NSData, NSDate, NSNumber, NSString, NSArray, NSDictionary)に限定されています。

と難しそうに書きましたが、このようなプロパティリストを作るにはProperty List Editorを使えば簡単です。Property List Editorは以前は独立したアプリケーションでしたが、Xcode 3.1ではエディタが組み込まれています。

 これを使って、以下の様に編集します。RootをDictionaryにしておいて、24個のkey/valueペアをひたすら追加していくだけです。

userDesaultsPlist.png