2011-05-30

くせ者NSUserDefaultsの使い方と理屈

NSUserDefaultsというと、手軽にアプリのユーザー設定の保持/変更ができて便利な機能です。設定アプリでユーザー設定値を変更して、それをアプリで読み込んで使えるので、まれにしか設定しないようなものは使いたい機能の1つです。
(多くの人が気づかないとAppleもなかば認めている「設定アプリ」からでないと出来ないという難点はありますが、画面とかいちいち作らなくてもいいので、便利ですよね。本当はアプリから呼び出せれば解決だと思うんですけど。。。iOS5でなんとか頼みます。。。)

ただ1つくせ者ぶりを発揮する場面が1カ所ありますのでその解説です。それは、Settings.bundleでroot.plistを作るときDefault Valueというデフォルト値を設定しますが、ここでデフォルト値を設定したにもかかわらず、該当アプリをロードしたあと設定アプリを1度起動しないと反映されない!ということです。設定アプリを1度も起動せずにその値を読もうとすると不定値が返ります。。。

症状としてはそんな感じです。使った事のある人は経験あると思います。不満な感じもググるとちょいちょいみかけます。

理屈はたぶんこう(憶測あり)です。設定アプリはロードされているアプリのroot.plistをみて、もし新規であればユーザー設定を新規にApplicationドメインに登録します。そのとき初期値はroot.plistに記述したDefault Valueが使われます。逆にいえば設定アプリしかApplicationドメインに登録する人がいないので、いくらroot.plistにDefault Valueを記述しても設定アプリを起動しないと登録できる人がいないともいえます。

で、このドメインが重要で、設定値は複数のドメインで同じ値を持つことができて、参照する優先順位が決まっています。本当は5つあるのですが、今回はApplicationドメインとNSRegistrationドメインに限定すると、Applicationドメインはユーザー設定値をファイルとして保持して永久保存されます。そしてNSRegistrationドメインよりも優先的に参照されます。NSRegistrationドメインはアプリが自分で設定できますが、アプリの終了とともに設定も消滅します。なので、必要であればアプリ起動ごとに毎回設定が必要です。

とここまで説明すればroot.plistのDefault valueがなぜ設定アプリを起動しないと反映されないかわかってきたと思います。対処方法としては、Applicationドメインに設定値が存在しない場合に読みにくるNSRegistartionドメインにデフォルト値をRoot.plistにかわってアプリが毎回設定しておいてあげればよい、ということになります。もちろんApplicationドメインに値があればNSRegistrationで設定した値は参照されないので、何も考えずに登録してもユーザーが設定した値が上書きされるなんてことにはなりません。優先順位もドメインも違いますからね。

ユーザー設定値を利用するクラスのinitializeクラスメソッド内部で、NSUserDefaultsクラスのregisterDefaultsメソッドで毎回デフォルトを書き込んでおけば良い事になります。

root.plistのDefault Valueを読み出して設定するようにがんばってコードを書くものありだと思います。そうすれば2重定義にならなくてすみます。

No comments:

Post a Comment