使い方は、まずサブクラスを作りプロパティを書く。
#import "LKUserDefaults.h" @interface UserDefaults : LKUserDefaults @property (strong, nonatomic) NSString* name; @property (strong, nonatomic) NSString* email; @end
後はプロパティへアクセスするだけで自動的に NSUserDefaultsへの読み書きが行われる。
UserDefaults* defaults = [UserDefaults sharedInstance]; defaults.name = @"Hoge"; // キー "name" で NSUserDefaults へ書き込み : NSString* name = defaults.name; // キー "name" の値を NSUserDefaults から読み出し
初期値を設定しておくこともできる。
- (void)registerDefaults { self.name = @"unkown"; self.name = @"unkown@xcatsan.com"; }
上記を定義しておくと、未設定の値を読みだした時にこの値が使われる。
定義可能なプロパティの型は NSUserDefaults と同じ。
@property (assign, nonatomic) BOOL boolValue; @property (assign, nonatomic) NSInteger integerValue; @property (assign, nonatomic) float floatValue; @property (assign, nonatomic) double doubleValue; @property (strong, nonatomic) NSString* stringValue; @property (strong, nonatomic) NSArray* arrayValue; @property (strong, nonatomic) NSArray* stringArrayValue; @property (strong, nonatomic) NSDictionary* dictionaryValue; @property (strong, nonatomic) NSData* dataValue; @property (strong, nonatomic) NSURL* urlValue; @property (strong, nonatomic) NSDate* dateValue;
オブジェクト型のプロパティの定義では ARC属性を strongにしておく。これは上記 registerDefaults の値を保持しておくため。
- - - -
実装のポイントは前回解説のように NSProxy をかましてプロパティへのアクセスをフックしているところにある。
前回記事:Cocoaの日々: NSUserDefaults の値をプロパティアクセスできるようにする
ライブラリでは汎用性のある部分を抽出して LKKeyValuStore と LKPropertyHook というクラスを作り、それを LKUserDefaults と LKUserDefaultsProxy が継承するようにしている。この為、若干読みづらいコードになっているかもしれない。今回は用意していないが NSUbiquitosKeyValueStore 用のクラスを用意すればiCloudの値も同じように使うことができる(たぶん)。
ハマったところとしては NSURL の格納部分。NSURL自体は NSuserDefaults のファイル形式である Property List には入れられない型だが NSUserDefaults では専用のメソッド(setURL:forKey:, URLforKey:)を用意していて、内部的に NSDataに変換して Property List へ入れている。この為、プロパティアクセスをフックしている今回の場合はプロパティの型情報を見てNSURLの時だけ適宜 NSDataへ変換してやる必要がある。この型判定がくせものでランタイム時に提供される情報(NSMethoSignatureなど)では引数/戻り値のクラスを知る方法を見つけることができなかった(オブジェクト=idであることしかわからなかった)。setterの場合は引数の型でそれが判断できるが getterの場合 Property List から取り出した NSData から判断するしかない。この為今回は NSUserDefaults から取得したオブジェクトが NSData の場合は無理やり URLforKey: を使って変換を掛けて、変換に成功したら NSURL として返し、失敗したら例外をキャッチして NSData のまま返すようにしている。ちょっとむりくりだけど。
なお形態としてはシングルトンになるが、複数クラスを定義したり、それらを継承関係にしたりすることができるようにしてある。ただしアクセス対象の NSuserDefaults は一つだけ(standardUserDefaults)。
課題・エンハンス案など
・現在はプロパティの先頭が小文字で始まることを前提にしている
・プロパティ名を途中で変更した場合、旧名の値が残り続ける(もしくは引き継げない)
・クラス定義毎に NSuserDefaults を作ることができるようにする
・擬似的な名前空間を作れるようにする(例えば VolumeSetting.name のようなキーで登録できるようにする)
何かあれば pull request をどうぞ。
Responses
Leave a Response