NSUserDefaultsの実際

このテキストは、 NSUserDefaults In Practice記事の翻訳です。 元の著者はDavid Smithです。 翻訳は著者の親切な許可を得て行われます。



NSUserDefaultsとは何ですか?



NSUserDefaults.hヘッダーファイルを開始するコメントは、クラスを非常によく説明しています。 このコメントを使用して開始します。

NSUserDefaultsは次のとおりです。



1)階層的

2)永続的(永続的)

3)プロセス間

4)場合によっては配布

キーバリュービューリポジトリ。 NSUserDefaultsは、ユーザー設定を保存するために最適化されています。


1)階層:



NSUserDefaultsには、このデータを探すデータストレージの場所のリストが含まれています。 このリストは「検索リスト」と呼ばれます。 「検索リスト」には、「スイート識別子」または「ドメイン識別子」と呼ばれる任意の文字列が含まれています。 リクエストが到着すると、NSUserDefaultsは、リクエストからのキーを含むアイテムが見つかるまで、またはリスト全体が渡されるまで、検索リスト内の各アイテムをチェックします。 リストには以下が含まれます。





注:「現在のホスト+現在のユーザー」設定はiOS、watchOS、およびtvOSには実装されていません。「for any user」設定は、基本的にこれらのオペレーティングシステムのアプリケーションには何も提供しません。



2)永続的(永続的):



NSUserDefaultsに保存されている設定は、特に指定がない限り、再起動からアプリケーションの再起動まで持続します。



3)プロセス間:



設定は、複数のプロセスから同時に読み書きできます(たとえば、アプリケーションとその拡張から)。



4)場合によっては、配布されます:



現時点では、学生向けの共有iPadモードでのみサポートを利用できます(Appleプログラムwww.apple.com/education/it-およそTransl。)。



NSUserDefaultsに保存されたデータは、分散させることができます(遍在-およそTransl。)。 クラウドを介したデバイス間の同期。 分散された「ユーザー設定」は、1つのiCloudアカウントにログインしているすべてのデバイスに自動的に転送されます。 設定を読み込むとき(フォームのメソッドを呼び出す-* ForKey :)、分散設定はローカル設定の前にチェックされます。 分散設定によるすべての操作は非同期です。 したがって、iCloudからのダウンロードが完了していない場合は、分散された設定ではなく、登録された設定を返すことができます。 分散設定は、アプリケーションのデフォルト構成ファイルで設定されます。



Key-Valueストレージ:



NSUserDefaultsは、プロパティリストオブジェクト(plistファイル)を保存します:NSString、NSData、NSNumber、NSDate、NSArray、NSDictionary-NSStringタイプのキーで識別可能。 これは、NSMutableDictionaryの動作に似ています。



ユーザー設定の保存用に最適化:



NSUserDefaultsは、頻繁に要求され、めったに変更されない比較的少量のデータを格納するように設計されています。 他の用途では、より適切なソリューションよりも操作が遅くなったり、メモリが消費されたりする可能性があります。



CoreFoundationでは、「App」名を含むCFPreferences関数は、NSUserDefaultsと同じ検索リストで機能します。 KVO(Key-Value Observing)メカニズムを使用したNSUserDefaultsの観察は、それらに保存されている任意のキーに対して可能です。 他のプロセスまたはデバイスからの変更を監視する場合、NSKeyValueObservingOptionPriorを使用してもKVOの動作には影響しません。



NSUserDefaultsの基本:99%



通常の状況では、NSUserDefaultsは非常に簡単です。



NSUserDefaultsから設定を読み取る:



コードの一部を制御する設定がある場合は、適切なgetterメソッド( -objectForKey:または特定のタイプのラッパーメソッドの1つ)を呼び出すだけです。

設定を取得するために何か他のことをする必要があることがわかった場合は、一歩下がって再度重量を量るべきです:



  1. NSUserDefaultsからの値のキャッシュは、読み取りが非常に高速であるため、通常は必要ありません。
  2. 値を読み取る前に-synchronizeを呼び出す必要はありません。
  3. 設定の目的はプログラムの動作を制御することであり、強制的に動作させることではないため、値の変更に応じたアクションはほとんど必要ありません
  4. 「値が設定されていない」場合を処理するコードを記述することも、デフォルト値を登録することができるため、通常は必要ありません(以下のデフォルト値の登録を参照)。


NSUserDefaultsへのカスタム設定の保存



同様に、ユーザーが設定を変更するときは、単に-setObject:forKey:(またはそのタイプ固有のラッパーの1つ)を呼び出すだけです。



何か他のものが必要であることがわかった場合は、おそらく必要ありません。 値を設定した後に-synchronizeを呼び出すことはほとんどありません(以下のプログラム間の設定の分離を参照)。 また、ユーザーは通常、設定を非常に迅速に変更できないため、あらゆる種類の「パッケージング」がパフォーマンスに役立ちます。 実際のディスク書き込みは非同期であり、NSUserDefaultsは変更を1つの書き込み操作に自動的にマージします。



デフォルト値を登録する



次のようなコードを記述したくなるかもしれません。



- (void) applicationDidFinishLaunching:(NSApplication *)app { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if (![defaults objectForKey:@"Something"]) { [defaults setObject:initialValue forKey:@"Something"]; } }
      
      





しかし、長期的には隠れた欠陥があります:初期値を変更したい場合、ユーザーが設定した値(保存したい)とあなたが設定した初期値(変更したい)を区別する方法がありません。 また、そうすることは一種の遅いです。 解決策は、 -registerDefaultsメソッドを使用することです。



 [[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"Something" : initialValue }];
      
      





これには多くの利点があります:





-registerDefaults メソッド 必要な回数だけ呼び出すことができます。 そして、彼に転送されたすべての辞書からのすべてのキーと値のペアが登録されます。 これにより、設定で機能するコードの隣に設定の登録を保持することができます。



プログラム間の設定の分離



それにも関わらず、難しい点の1つは、実行中の複数のプロセス間、たとえばアプリケーションとその拡張機能間、または2つ以上のアプリケーション間(macOS上)で設定を共有する必要があることです



昔(良い/悪い)の時代には、アプリケーションがサンドボックスに配置される前でさえ、すべてが非常に簡単でした。両方のプロセスで同じ名前の[[NSUserDefaults alloc] initWithSuiteName:]を使用すると、これらのプロセスは同じ設定を共有します。 用語に関する注記:「ドメイン」と「セット名」は同じ意味で使用されます。 両方の用語は、単に設定リポジトリを識別する任意の文字列を意味しています。



サンドボックスの世界、最新のmacOSおよびすべてのiOSの世界では、NSUserDefaultsは最初はアプリケーションのサンドボックスでの作業に制限されています。 -initWithSuiteName:を使用すると、新しい設定ストアだけが得られますが、これは依然として分離不可能です。 共有するには、2つのことが必要です。



  1. 設定に対応する共有コンテナを作成します
  2. セットの名前としてこのコンテナの識別子を使用します。これは、セットの作成時にNSUserDefaultsに渡されます( -initWithSuiteNameメソッドを使用 -約Transfer )。 詳細は説明しませんが、 ここでは関連するドキュメントを見つけることができます。 グループにアプリケーションまたはアプリケーション拡張機能を追加するとすぐに、グループIDと同じ名前のセットが自動的に共有されます。


プロセスの1つが共有設定をセットアップし、それを他のプロセスに通知する場合、おそらく-synchronizeメソッドの呼び出しが役立つ非常にまれな状況の1つです。 これはブロッキング方式です。 そこから戻った後、他のプロセスによる設定の読み取りが、古い値ではなく新しい値を返すことを保証します。 iOS 9.3以降またはmacOS Sierra以降で実行されているアプリケーションの場合、記載されている状況でも-synchronizeは必要ありません(または推奨されません)。 設定のKVO監視はプロセス間で機能するようになったため、読み取りプロセスは値の変化を直接観察できます。



その結果、これらのオペレーティングシステムで実行されているアプリケーションは、通常、 -synchronizeを呼び出すことはありません。



私の主な推奨事項:可能な限り少ない設定を分離する-単に値が外部で変更されない場合にコードが理解し、維持しやすいためです。



トランザクションはNSUserDefaultsに実装されていないため、複数の変更の結果が一度に読み取り可能になることを保証する方法はありません。 別のアプリケーションでは、後続の変更が完了する前に最初の変更の結果が表示される場合があります。



デバイス間の設定の分離



分散(つまり、iCloudに保存された)設定は、トレーニング用の共有iPadモードでのみサポートされるようになりました。 したがって、これらはこの一般的な議論の範囲外です。 現在、トレーニングモード以外の分散データストレージでは、NSUserDefaultsを使用する必要がありますが、NSUbiquitousKeyValueStoreです。 トラップと注意のセクションでは、分散設定の重要な瞬間がいくつか言及されています。



トラップと注意事項:StackOverflowはこのセクションを参照します



最前線に置かれたシンプルさにもかかわらず、あなた自身のために問題を引き起こす多くの方法があります。



NSUserDefaultsは長年にわたって大幅に改善されました。 以下のリストは、iOS 10およびmacOS Sierraに関連していますが、古いシステムではより長くなるはずであり、今後短くなる可能性があります。





高度なNSUserDefaults:おそらく必要ないでしょう



驚きに満ちたバッグは、NSUserDefaultsではあまり使用されません。 ミツバチが含まれる場合があります。





NSUserDefaultsのパフォーマンストレードオフ:高速化



一般に、NSUserDefaultsのパフォーマンスは十分に優れているので、心配する必要はありません。 ただし、問題が発生した場合に注意する必要のあるいくつかのポイントがあります(「ツール」ユーティリティなどのプロファイラーを使用して確認してください!)



設定を初めて読み込むと、セット全体がメモリにロードされます。 遅いシステムでは、これにはかなりの時間がかかります。 これの結果:



  1. 設定に大量のデータを保存しないでください。それらは一度に読み込まれます
  2. 各セットには独自のブートストラップが必要になるため、大量のプリセットを積み重ねないでください。


ドメインに設定がない場合でも、この事実を検出するためのオーバーヘッドコストがあります。 たとえば、「デバッグログを有効にする」オプションがある場合、通常、個別の「ロギング」セットよりも標準設定でメモリに保存する方が高速で経済的です。



すでにロードされた設定の読み取りは非常に高速です。2012MacBook Proでは約0.5マイクロ秒です。 特定の事柄はキャッシュを無効にし、リロードが必要になる場合があります。セットが別のプロセスと共有されている場合、いずれかのプロセスで設定を設定すると、両方のキャッシュが無効になります。 非共有設定のより一般的なケースでは、インストール後に設定を読み取ると小さなオーバーヘッドが発生しますが、キャッシュの完全な再構築は発生しません。 ここからの結論:



  1. 可能であれば、分離を避ける
  2. 可能な限り、インストールを最小限に抑える
  3. 常に無料で読むことができます。


異なる値であっても、1つのキーを複数インストールすると、多くの異なるキーをインストールするよりも大幅に高速になります。 これにより、リアルタイムウィンドウサイズの保存などの場合にNSUserDefaultsが高速になります。



セットアップによって引き起こされる作業のほとんどは非同期です。 ただし、非同期書き込みの進行中は読み取りがブロックされる可能性があります。 -synchronize呼び出しもブロックしています。 大規模な分割セットの断続的なインストールと読み取り操作は、パフォーマンスチューニングの最悪のケースです。



設定内のコレクションに値を設定すると、コレクション全体がインストールされます(明らかに、「コレクションの変更されていない部分も-ほぼ翻訳」に重点が置かれます)。 部分的な書き込みサポートは、最上位キーに対してのみ機能します。



値を設定すると(最終的には非同期であり、別のプロセスで少し遅れて発生します)、変更がわずかであってもplist全体がディスクに書き込まれます。 特に頻繁に変更する場合は、大量のデータを保存しないでください。



tic-tac-toeプログラムの恐ろしい恐怖は、NSUserDefaultsの悲しい終わりにつながります



All Articles