iOSのアプリを作っていると,アプリのデータを保存するときにNSUserDefaultsを使うことがあると思う.
ローカルに保存するし,DBを導入するほどではないときに非常に便利に使わせてもらっている.
しかし,NSUserDefaultsには少し制限があって,たとえば保存しておける型に縛りがあったりする.
もちろんNSDictionary
は保存できるのだが,内部にNSNull
を含むNSDictionary
を保存しようとすると,
saving dictionary error Property list invalid for format (property lists cannot contain objects of type 'CFNull')
こんなエラーを吐かれる.
で,これは言われている通りで,CFNull
のままじゃ保存できないんですね.
じゃぁNullのところを別のものに書き換えればいい
なんでもいいんですけど,Dictionary
だしString型の""
に書き換えてみます.
func cleanDictionary(dict: NSMutableDictionary)->NSMutableDictionary { var mutableDict: NSMutableDictionary = dict.mutableCopy() as NSMutableDictionary mutableDict.enumerateKeysAndObjectsUsingBlock { (key, obj, stop) -> Void in if (obj.isKindOfClass(NSNull.classForCoder())) { mutableDict.setObject("", forKey: (key as NSString)) } else if (obj.isKindOfClass(NSDictionary.classForCoder())) { mutableDict.setObject(self.cleanDictionary(obj as NSMutableDictionary), forKey: (key as NSString)) } } return mutableDict } var userDictionary = cleanDictionary(dictionary as NSMutableDictionary) var userDefaults = NSUserDefaults.standardUserDefaults() userDefaults.setObject(userDictionary, forKey: "dictionary")
こんな感じの関数を作ります.
で,ここにNSMutableDictionary型に変換したDictionary
を突っ込んであげると,見事NSNull型をString型の""
に変更してきます.
ちなみに,ちゃんと再帰処理させてあるので,Dictionary
の中にDictionary
を突っ込む木構造でも問題なくすべてNSNull
を掃除してくれます.
二行目で受け取ったdict
をいきなりmutableCopy
しています.
受け取っている時点で,型指定がNSMutableDictionary
なので大丈夫そうな気がするんですが,これを挟まずにやると,setObject
メソッドを走らせた時に,
reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'
と怒られます.
そのため対処療法ですが,強制的に変換しています.
多分これ,NSUserDefaults以外にもNSFileManagerなどでも使う場面があるかと思うので,押さえておくと便利です.