NSUserDefaultsとは何ですか?
NSUserDefaults.hヘッダーファイルを開始するコメントは、クラスを非常によく説明しています。 このコメントを使用して開始します。
NSUserDefaultsは次のとおりです。
1)階層的
2)永続的(永続的)
3)プロセス間
4)場合によっては配布
キーバリュービューリポジトリ。 NSUserDefaultsは、ユーザー設定を保存するために最適化されています。
1)階層:
NSUserDefaultsには、このデータを探すデータストレージの場所のリストが含まれています。 このリストは「検索リスト」と呼ばれます。 「検索リスト」には、「スイート識別子」または「ドメイン識別子」と呼ばれる任意の文字列が含まれています。 リクエストが到着すると、NSUserDefaultsは、リクエストからのキーを含むアイテムが見つかるまで、またはリスト全体が渡されるまで、検索リスト内の各アイテムをチェックします。 リストには以下が含まれます。
- MCX(OS Xの管理クライアント-OS X管理クライアント)を介して構成プロファイルまたはネットワーク管理者によって設定された管理(「強制」)設定
- コマンドライン引数
- クラウド内の現在のドメインの設定
- 現在のホスト上の現在のドメイン、現在のユーザーの設定
- 任意のホスト上の現在のドメイン、現在のユーザーの設定
- -addSuiteNamedを呼び出して追加された設定:
- 現在のホスト上の、現在のユーザーのすべてのアプリケーションのグローバル設定
- 任意のホスト上の現在のユーザーのすべてのアプリケーションのグローバル設定
- 現在のホスト上のすべてのユーザーの現在のドメインの設定
- 現在のホスト上のすべてのユーザーのすべてのアプリケーションのグローバル設定
- -registerDefaultsを介して登録された設定:
注:「現在のホスト+現在のユーザー」設定は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つ)を呼び出すだけです。
設定を取得するために何か他のことをする必要があることがわかった場合は、一歩下がって再度重量を量るべきです:
- NSUserDefaultsからの値のキャッシュは、読み取りが非常に高速であるため、通常は必要ありません。
- 値を読み取る前に-synchronizeを呼び出す必要はありません。
- 設定の目的はプログラムの動作を制御することであり、強制的に動作させることではないため、値の変更に応じたアクションはほとんど必要ありません
- 「値が設定されていない」場合を処理するコードを記述することも、デフォルト値を登録することができるため、通常は必要ありません(以下のデフォルト値の登録を参照)。
NSUserDefaultsへのカスタム設定の保存
同様に、ユーザーが設定を変更するときは、単に-setObject:forKey:(またはそのタイプ固有のラッパーの1つ)を呼び出すだけです。
何か他のものが必要であることがわかった場合は、おそらく必要ありません。 値を設定した後に-synchronizeを呼び出すことはほとんどありません(以下のプログラム間の設定の分離を参照)。 また、ユーザーは通常、設定を非常に迅速に変更できないため、あらゆる種類の「パッケージング」がパフォーマンスに役立ちます。 実際のディスク書き込みは非同期であり、NSUserDefaultsは変更を1つの書き込み操作に自動的にマージします。
デフォルト値を登録する
次のようなコードを記述したくなるかもしれません。
- (void) applicationDidFinishLaunching:(NSApplication *)app { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; if (![defaults objectForKey:@"Something"]) { [defaults setObject:initialValue forKey:@"Something"]; } }
しかし、長期的には隠れた欠陥があります:初期値を変更したい場合、ユーザーが設定した値(保存したい)とあなたが設定した初期値(変更したい)を区別する方法がありません。 また、そうすることは一種の遅いです。 解決策は、
[[NSUserDefaults standardUserDefaults] registerDefaults:@{ @"Something" : initialValue }];
-
ここではディスクに何も保存されないため、ユーザーが設定した値と混同することはありません -
登録された値は、ユーザーが設定した値によって自動的に上書きされます。 T.O. if値の設定をラップして実行する必要があるかどうかを確認する必要はありません -
ディスクへの書き込みなし:アプリケーションの起動時の速度低下、ディスクの摩耗はありません。
プログラム間の設定の分離
それにも関わらず、難しい点の1つは、実行中の複数のプロセス間、たとえばアプリケーションとその拡張機能間、または2つ以上のアプリケーション間(macOS上)で設定を共有する必要があることです
昔(良い/悪い)の時代には、アプリケーションがサンドボックスに配置される前でさえ、すべてが非常に簡単
サンドボックスの世界、最新のmacOSおよびすべてのiOSの世界では、NSUserDefaultsは最初はアプリケーションのサンドボックスでの作業に制限されています。
- 設定に対応する共有コンテナを作成します
- セットの名前としてこのコンテナの識別子を使用します。これは、セットの作成時にNSUserDefaultsに渡されます( -initWithSuiteNameメソッドを使用: -約Transfer )。 詳細は説明しませんが、 ここでは関連するドキュメントを見つけることができます。 グループにアプリケーションまたはアプリケーション拡張機能を追加するとすぐに、グループIDと同じ名前のセットが自動的に共有されます。
プロセスの1つが共有設定をセットアップし、それを他のプロセスに通知する場合、おそらく
その結果、これらのオペレーティングシステムで実行されているアプリケーションは、通常、
私の主な推奨事項:可能な限り少ない設定を分離する-単に値が外部で変更されない場合にコードが理解し、維持しやすいためです。
トランザクションはNSUserDefaultsに実装されていないため、複数の変更の結果が一度に読み取り可能になることを保証する方法はありません。 別のアプリケーションでは、後続の変更が完了する前に最初の変更の結果が表示される場合があります。
デバイス間の設定の分離
分散(つまり、iCloudに保存された)設定は、トレーニング用の共有iPadモードでのみサポートされるようになりました。 したがって、これらはこの一般的な議論の範囲外です。 現在、トレーニングモード以外の分散データストレージでは、NSUserDefaultsを使用する必要がありますが、NSUbiquitousKeyValueStoreです。 トラップと注意のセクションでは、分散設定の重要な瞬間がいくつか言及されています。
トラップと注意事項:StackOverflowはこのセクションを参照します
最前線に置かれたシンプルさにもかかわらず、あなた自身のために問題を引き起こす多くの方法があります。
NSUserDefaultsは長年にわたって大幅に改善されました。 以下のリストは、iOS 10およびmacOS Sierraに関連していますが、古いシステムではより長くなるはずであり、今後短くなる可能性があります。
- NSUserDefaultsによって返されるコレクションは、可変コレクションを保存した場合でも常に不変です。
- 可変コレクションをNSUserDefaultsに保存してから変更しても、変更された値は保存されません。
- 設定はplistファイルではなく、plistファイルに保存するのは一般的なケースです。 plistファイルに直接干渉すると、予測できない結果が生じる可能性があります。 代わりに、NSUserDefaults、CFPreferences、コマンドラインのデフォルトユーティリティを使用してください。 「デフォルトのインポート」と「デフォルトのエクスポート」を使用して、標準のplistファイルと設定の間で「全体」を変換できます。
- Plistファイルは設定ではありません。 plutilまたはNSPropertyList APIを使用して、任意のplistファイルを操作しますが、設定またはNSUserDefaultsは操作しません
- NSUserDefaultsは、plistが保存できる型のみを保存できます。 任意のオブジェクトを保存する場合は、NSKeyedArchiverまたは同様のものを使用して、最初にNSDataを取得する必要があります。 これは多くの場合、ユーザー設定以外の何かを保存しようとしていることを意味します。
- iOS 9.3 / macOS Sierraより前は、KVOはstandardUserDefaultsでのみ動作します
- iOS 9.3 / macOS Sierraより前は、KVOは他のアプリケーションによる変更について通知しません
- NSUserDefaultsDidChangeNotificationは、他のアプリケーションによって行われた変更について通知しません
- -registerDefaultsメソッド: NSUserDefaultsの各インスタンスで実行されます。呼び出したインスタンスだけでなく
- + resetStandardUserDefaultsメソッドは、特に有用なことは何もしません
- VolatileDomainメソッドも特に有用なことは何もしません。
- -setPersistentDomain:forName:メソッドを使用することは、複数のスレッドが設定を行う場合に避けるのが困難です。 このような状況の理由は次のとおりです。
- スレッド 1は-persistentDomainForName:を呼び出し、現在の設定のスナップショットを取得します
- スレッド 2は、キー「A」に対して-setObject:forKey:を呼び出します
- ストリーム1は、受信したスナップショットのコピーを作成し、「B」キーの値を変更してから、 -setPersistentDomain:forName:を呼び出します。
Stream "1"は辞書全体を一度にインストールし、この辞書にはキー "A"の新しい値がないため、キー "A"の値を変更しても失われます。
- 設定を設定し、すぐにexit()またはabort()を呼び出した場合( -terminateなどではない)、設定した値が失われる可能性があります。 CFPreferencesAppSynchronize()を使用して、出口を安全にすることができます。
- プロセスまたはデバイス間でKVOとともに設定を使用する場合は、変更に応じて設定を設定しないでください。 そうしないと、2つ以上のプロセス/デバイスが互いに行われた変更に常に応答し、バッテリーとトラフィックを消費する「サイクル」が発生する可能性があります(分散設定の場合)
- 通常の設定とは異なり、配布された設定はクラウドに保存され、利用可能になるという保証はありません。 したがって、ダウンロードがまだ完了していない場合は、分散設定を読み取ると、クラウドから実際の値ではなく登録された値が返される場合があります(順番に時間がかかります)。 これでアプリケーションの準備が整います。
高度なNSUserDefaults:おそらく必要ないでしょう
驚きに満ちたバッグは、NSUserDefaultsではあまり使用されません。 ミツバチが含まれる場合があります。
- セットを追加/削除するためのメソッドは、NSUserDefaultsの別のインスタンスを取得し、それらが呼び出されるインスタンスに効果的に挿入します( -addSuiteNamedのドキュメント :「指定されたドメイン名を受信者検索リストに挿入」-およその転送)。 はい、別のプロセスと共有する設定に自動的にアクセスできることを意味します。 いいえ、プロセス間で変更可能な状態への目に見えないアクセスは怖いので、おそらくこれを行うべきではありません。
- PersistentDomain(永続ドメイン)メソッドは、 -initWithSuiteName:が現れるまで、別のアプリケーションの設定を操作する唯一の方法でした。 現代の世界に残っている主なアプリケーションは、呼び出し-removePersistentDomainForName:であり、ドメイン内のすべての設定を削除します。
- -objectIsForcedForKeyメソッド:この設定が構成プロファイル(iOS)またはMCX(macOS)によってオーバーライドされているかどうかを確認できます
- -setURL:forKey:メソッドは、ボックスに書かれていることを行います。 非plistタイプを保存する唯一のNSUserDefaultsメソッドであるという点でユニークです。 NSURLを保存する場合は、 -setObject:forKey:の代わりに-setURL:forKey:を使用する必要があります。
- さまざまな型のゲッターが読み取り値の暗黙的な変換を実行します。 たとえば、 -integerForKey:を使用すると「1.0」は「1」として読み取られ、 -boolForKey:を使用すると「YES」と読み取られます。
- "-key value"(-key value)の形式のプログラムのコマンドライン引数は、設定をオーバーライドします。これは、テストに役立つ場合があります。 ところで、Xcodeのプロジェクト図でこれらの引数を設定できます
- すべてのアプリケーションが読み取るグローバル設定を書き込むには、コマンド「defaults write -g」を使用できます
NSUserDefaultsのパフォーマンストレードオフ:高速化
一般に、NSUserDefaultsのパフォーマンスは十分に優れているので、心配する必要はありません。 ただし、問題が発生した場合に注意する必要のあるいくつかのポイントがあります(「ツール」ユーティリティなどのプロファイラーを使用して確認してください!)
設定を初めて読み込むと、セット全体がメモリにロードされます。 遅いシステムでは、これにはかなりの時間がかかります。 これの結果:
- 設定に大量のデータを保存しないでください。それらは一度に読み込まれます
- 各セットには独自のブートストラップが必要になるため、大量のプリセットを積み重ねないでください。
ドメインに設定がない場合でも、この事実を検出するためのオーバーヘッドコストがあります。 たとえば、「デバッグログを有効にする」オプションがある場合、通常、個別の「ロギング」セットよりも標準設定でメモリに保存する方が高速で経済的です。
すでにロードされた設定の読み取りは非常に高速です。2012MacBook Proでは約0.5マイクロ秒です。 特定の事柄はキャッシュを無効にし、リロードが必要になる場合があります。セットが別のプロセスと共有されている場合、いずれかのプロセスで設定を設定すると、両方のキャッシュが無効になります。 非共有設定のより一般的なケースでは、インストール後に設定を読み取ると小さなオーバーヘッドが発生しますが、キャッシュの完全な再構築は発生しません。 ここからの結論:
- 可能であれば、分離を避ける
- 可能な限り、インストールを最小限に抑える
- 常に無料で読むことができます。
異なる値であっても、1つのキーを複数インストールすると、多くの異なるキーをインストールするよりも大幅に高速になります。 これにより、リアルタイムウィンドウサイズの保存などの場合にNSUserDefaultsが高速になります。
セットアップによって引き起こされる作業のほとんどは非同期です。 ただし、非同期書き込みの進行中は読み取りがブロックされる可能性があります。
設定内のコレクションに値を設定すると、コレクション全体がインストールされます(明らかに、「コレクションの変更されていない部分も-ほぼ翻訳」に重点が置かれます)。 部分的な書き込みサポートは、最上位キーに対してのみ機能します。
値を設定すると(最終的には非同期であり、別のプロセスで少し遅れて発生します)、変更がわずかであってもplist全体がディスクに書き込まれます。 特に頻繁に変更する場合は、大量のデータを保存しないでください。
tic-tac-toeプログラムの恐ろしい恐怖は、NSUserDefaultsの悲しい終わりにつながります