NSUserDefaultsに潜む危険

そういえばNSUserDefaultsのメモリの扱い全然知らないなと思って、ネットで調べてみても出なかったので 自分で検証して見ました。 結果、NSUserDefaultsの使用容量によってはアプリがすごい重くなったり、下手をすれば起動自体しなくなることがある!!ってことがわかりました! 結構驚きです。

いつデータはロードされるのか

NSUserDefaultsのメモリ関係で一番気になるのはいつデータがロードされて、いつ解放されるかです。 それを調べるために次のような手順のソースでメモリを監視して見ました。

  1. standardUserDefaultsを呼び出すだけ
1
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
  1. 値1個を保存
1
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; id value = [userDefaults valueForKey:@"key"];
  1. すでに保存されている値1個を取り出す
1
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; id value = [userDefaults valueForKey:@"key"];
  1. 値1000個を保存
1
NSUserDefaults \*userDefaults = [NSUserDefaults standardUserDefaults]; for(int i = 0; i < MAX_KEY; i++){ NSAutoreleasePool \*pool = [[NSAutoreleasePool alloc] init]; NSString \*value = [NSString stringWithFormat:@"value No.%d",i]; NSString \*key = [NSString stringWithFormat:@"%d",i]; [userDefaults setValue:data forKey:key]; [pool release]; }
  1. すでに保存されている値10000個を取り出す
1
NSUserDefaults \*userDefaults = [NSUserDefaults standardUserDefaults]; for(int i = 0; i < MAX_KEY; i++){ NSAutoreleasePool \*pool = [[NSAutoreleasePool alloc] init]; NSString \*key = [NSString stringWithFormat:@"%d",i]; NSString \*value = [userDefaults valueForKey:key]; [pool release]; }

(値はすべて英数10文字です。) さて、その結果は次のようになりました。

コード前後での
メモリ使用量の増加分

standardUserDefaultsを呼び出すだけ

5KB

値1個を保存

5KB

値1個を取り出す

3KB

値1000個を保存

85KB

値1000個を取り出す

5KB

保存時にはそれなりにメモリを食うけれども、読み込み時にはメモリを全然使いませんでした。 これは、

  • 起動→保存→読み込み
  • 起動→読み込み

のどちらの時も同じでした。 また、保存されているデータがどんなに大きくなってもstandardUserDefaultsを呼び出すだけではメモリをほとんど消費しません。 さて、データはどのタイミングでロードされてるんでしょう・・・・?

データロードのタイミング

色々試していて分かった結論を言うとデータはアプリ起動時に勝手に読み込まれます データの数を変えながら試してみると、起動時のメモリ消費量がNSUserDefaultsの容量に応じて増えてました。 NSUserDefaultsのデータは すべてアプリ起動時に全て読み込まれるようです。

NSUserDefaultsに登録したデータ数

アプリ起動直後のメモリ使用量

0

600Kb

1000個

670KB

2000個

770KB

10000個

3300KB

また、一度ロードされたデータはいつまでも解放されないようです。 これはシングルトンの性質上仕方ないかなと思います。

NSUserDefaultsが起動時に全てロードするのは結構危険です。 NSUserDefualtsにデータを保存しすぎると、アプリが使えるメモリが起動時からずっと圧迫されてしまうのです。 仮にアプリが10MBまでしかメモリをつかえないとしたとき NSUserDefaultsで6MBのデータを保存してしまったら アプリはのこり4Mbのメモリですべての動作を行わないといけなくなるのです。 NSUserDefaultsに大量のデータを入れると全体的にアプリが重くなることもありえますので、扱いには注意が必要そうです。 ちなみに、色々試してデータを沢山入れているとアプリが起動しなくなることもありました。 おそらく起動に必要なメモリすらも確保出来なかったんだと思います・・・・。