Help us understand the problem. What is going on with this article?

意外にパワフル!?NSUserDefaultsの書き込み、読み込み速度について

More than 5 years have passed since last update.

NSUserDefaultsはデータの永続化にとても便利なクラスですが、大量のデータを扱う際はCoreDataなどデータベースに基づくクラスを使った方メモリ的の使用量が押さえられ、検索速度的にも有利です。

では、どの程度までのサイズのデータでしたらNSUserDefaultsで無理無く扱うことができるのでしょうか。
この疑問を自分なりに解決するために、以下のコードを書いてみました。

UserDefaultsSample.m
- (void)viewDidLoad
{
    [super viewDidLoad];

    mArray = [NSMutableArray new];

    NSArray *array = @[@"赤",
                       @"青",
                       @"緑",
                       @"黄",
                       @"白"];

    int repeatNumber = 10000;
    for (int i = 0; i<repeatNumber; i++) {
        NSMutableString *string = [NSMutableString new];
        for (int j = 0; j<100; j++) {
            int randNum = arc4random()%[array count];
            [string appendString:array[randNum]];
        }
        [mArray addObject:string];
    }
}

-(IBAction)saveTapped:(id)sender{
    NSDate *beforeDate = [NSDate date];
    NSUserDefaults *uD = [NSUserDefaults standardUserDefaults];
    [uD setObject:mArray forKey:@"Array"];
    [uD synchronize];
    NSTimeInterval interval =fabs([beforeDate timeIntervalSinceNow]);
    NSLog(@"Saving took %f seconds!", interval);
}

-(IBAction)loadTapped:(id)sender{
    NSDate *beforeDate = [NSDate date];
    NSUserDefaults *uD = [NSUserDefaults standardUserDefaults];
    NSMutableArray *loadedArray = [uD objectForKey:@"Array"];
    NSTimeInterval interval =fabs([beforeDate timeIntervalSinceNow]);
    if (loadedArray) {
        NSLog(@"Loading took %f seconds!", interval);
    }
}

-(IBAction)changeTapped:(id)sender{
    NSDate *beforeDate = [NSDate date];
    mArray[0] = @"ABC";
    NSTimeInterval interval =fabs([beforeDate timeIntervalSinceNow]);
    NSLog(@"Changing took %f seconds!", interval);
}

全角で100文字のNSMutableString型のランダムな文字列を作成し、指定した数(この場合は1万個)配列に格納します。
Storyboard上のボタンを押すことにより、この配列がそれぞれ保存、読み込み、変更されます。
それぞれの所用時間はNSDateを使うことにより測定されます。
実験はiPhone5の実機を使い行いました。

結果は以下の通りです。
userdefaultstime.png
1000要素の配列(10万文字)では、保存に僅か0.02秒程度しかかかりませんでした。
さすがに50万要素の配列(5000万文字)では、保存に8.1秒ほどかかってしまいました。

要素数が増えても、読み込み速度は変わりませんでした。
※ご指摘がありました。これはNSUserDefaultsはアプリ起動時にメモリ上へ読み込みが行われるためです。手元の環境で僕も確認しました。読み込み速度の測定は、起動時に行う必要があります。

要素の変更に要する時間も要素数が増加ても変化しませんでしたが、これはNSUserDefaultsを介さずにメモリ上で処理が行われているからです。

また、サイズは配列が使用したメモリなのであまりNSUserDefaultsとは関係ありませんが、メモリ使用量の目安にはなるかと思います。1文字当たり3byteで計算しました。

新書1冊が15万文字程度らしいですので、これを保存するのに要する時間は0.03秒程度だと思われます。
ただ、配列の要素当たりの文字数によってこの値は多少変化するとは思われます。
例えば1日10ツイート(1400文字)を3年間保存したと仮定すると、
150万文字ほどを扱うことになります。
1万要素(100万文字)で0.17秒程度なので、0.3秒以内には保存処理が完了することになります。
この場合、データベースを使用するのも有りですが、シンプル化のためにNSUserDefaultsを使用することを考えていいかもしれません。

また、10万要素(1000万文字)を超えるとメモリの占有量が大きくなり、保存処理に時間もかかるので、
データベースの独壇場になりそうです。

その他、データベースを使うことで便利な検索機能を使用できるなどのメリットもありますのでそれらを含めて検討する必要がありますね。

今回の実験で、NSUserDefaultsの以外なパワフルさには驚かされました。

yuky_az
「ヒトとAIの共生」がミッションの会社、SAI-Lab株式会社の代表取締役。 東北大学大学院理学研究科修了。理学博士(物理学)。 著書に「はじめてのディープラーニング」、「No.1スクール講師陣による 世界一受けたいiPhoneアプリ開発の授業」。オンライン教育プラットフォームUdemyで、2万人近くを指導する人気講師。
https://sai-lab.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away