19

Mac OS X用Cocoaプログラミング、第3版、 202ページ(第13章)によると:

アプリケーションのいくつかのクラスで、デフォルトの登録、読み取り、および設定を行います。常に同じ名前を使用するようにするには、これらの文字列を1つのファイルで宣言してから、そのファイルを名前を使用する任意のファイルに#importする必要があります。これを行うにはいくつかの方法があります。たとえば、Cプリプロセッサの#defineコマンドを使用できますが、ほとんどのCocoaプログラマーはこの目的でグローバル変数を使用します。

これは本当に正しいベストプラクティスですか?グローバル変数?それは私には非常識に思えます-私がこれまでに教えられたすべてに反します。

より良い設計は、これらが定義された単純なシングルトンクラスでしょうか?それとも、グローバル化することが本当に正しいベストプラクティスですか?多くの人がシングルトンをきれいなドレスを着たグローバルだと考えていることを考えると、どちらよりも良いパターンはありますか?

4

7 に答える 7

63

明確にするために、インライン文字列定数 (リファクタリングが困難でコンパイル時のチェックなし) または #defines (コンパイル時のチェックなし) の代わりに、不変のグローバル変数を作成することをお勧めします。これがあなたがそうする方法です...

MyConstants.h で:

extern NSString * const MyStringConstant;

MyConstants.m で:

NSString * const MyStringConstant = @"MyString";

次に、他の .m ファイルで:

#import "MyConstants.h"

...
[someObject someMethodTakingAString:MyStringConstant];
...

このようにして、文字列定数のスペルが間違っていないことをコンパイル時にチェックし、定数を比較する際に文字列の等価性[1]ではなくポインターの等価性をチェックでき、定数には実行があるため、デバッグが容易になります。 -時間文字列値。

[1] この使用法では、基本的にポインター値を定数として使用しています。これらの特定の整数が、デバッガーで使用できる文字列を指している場合もあります。

于 2008-12-03T23:59:05.857 に答える
18

それをグローバル変数と呼ぶことは技術的には正しいですが、誤解を招きます。

これはグローバル定数です。スコープはグローバルですが定数であるため、グローバル変数が悪いという意味では悪くありません。

グローバル定数がどのように一般的で、安全で、多数あるかを示すために、グローバル定数の次の例を検討してください。

  • プログラム内のすべてのクラス
  • #define ごと
  • すべての列挙型
  • Cocoa によって宣言されたほぼすべての名前 ( のようなまれなグローバル変数を除くNSApp)。

グローバル定数について心配する必要があるのは、その名前が一般的すぎる場合だけです (グローバル名前空間を汚染する可能性があります)。したがって、競合する可能性のある名前は使用しないでください (常に接頭辞を使用し、常にタスク固有の名前にしますNSKeyValueObservingOptionNew)。

于 2008-12-03T23:37:20.830 に答える
18

グローバル変数またはシングルトンは、ここで同じことを達成します。両方とも、Cocoa で「キー」名を変換するために使用できます。これは、スペルが間違っていてもコンパイラ エラーがスローされないためです。それが主な目的です。グローバル変数は、タイピングが少なくて済むので、少し簡単です。

これを行う代わりに:

[myArray setObject:theObject forKey:MyGlobalVariableKeyName];

次の行に沿って何かをする必要があります。

[myArray setObject:theObject 
            forKey:[[MySingletonVariableClass getInstance] myVariableKeyName];

グローバル変数は、同じ効果に対して基本的に入力が少なくなります。

于 2008-12-03T18:26:15.170 に答える
3

コンパイル時に設定され、変更されない定数グローバルは、私には受け入れられます。文字列をハードコーディングしても、それは同じことであり、コンパイラによって隠されているだけです。ペストのような変更可能なグローバルは避けます。

Apple 自身も同じ手法を使用していることを思い出してください。私が定義していると思っていた定数の多くは、実際には定数です。ヘッダーに到達できるがフレームワークに到達できない場合、リンク エラーが発生します。

于 2008-12-03T18:37:20.420 に答える
1

結局のところ。3つのファイルを思いつきました。

定数.h

#define def_key(name) extern NSString *const name
#define def_int(name, value) extern int const name
#define def_type(type, name, value) extern type const name

#include "ConstantsDefs.h"

定数.m

#import "Constants.h"

#undef def_key 
#define def_key(name) NSString *const name = @#name

#undef def_int
#define def_int(name, value) int const name = value

#undef def_type
#define def_type(type, name, value) type const name = value

#include "ConstantsDefs.h"

ConstantsDefs.h

def_key(kStringConstant);
def_int(kIntConstant, 313373);
def_type(float, kFloatConstant, 313373.0f);
于 2012-02-11T09:04:16.213 に答える
1

@Barry Warkと@Matt Gallagherの優れた回答、および私の最初の回答(この回答の最後を参照)に基づいて構築する3番目のアプローチがあります。それは、変数名を1回だけ入力することを保証するマクロ/インクルードの組み合わせを使用することです。したがって、.h ファイルと .m ファイルの両方に同時に含まれます。

<編集>

「常に別の方法があります...」

追加のヘッダー ファイルを使用せずに、さらに単純にする方法を考えた後、ネストされたマクロを使用したより簡潔なアプローチを次に示します。

.h ファイル内

#define defineKeysIn_h_File(key)   extern NSString * const key; 
#define defineKeysIn_m_File(key)   NSString * const key = @#key; 


#define myKeyDefineKeys(defineKey) \
/**start of key list*/\
defineKey(myKeyABC);\
defineKey(myKeyXYZ);\
defineKey(myKey123);\
/*end of key list*/

myKeyDefineKeys(defineKeysIn_h_File);

.m ファイルで

myKeyDefineKeys(defineKeysIn_m_File);

実装メモ

これは複数のヘッダーで複数回使用できますが、「myKeyDefineKeys」の名前を一意になるように変更する必要があります。定義しているキーと同じプレフィックスを付けることをお勧めします - 私が使用した例のために」 myKey」全体。

別のファイルでは、「myOtherKeyDefineKeys」を使用する場合があります。

また、defineKeysIn_h_File マクロと defineKeysIn_m_File マクロをいじらないでください。定義が変更されたという警告が表示されます。

<編集終了>

元の回答、まだ有効ですが、洗練されていません

まず、vanilla.h ファイルを作成し、デフォルトの #ifdef などを削除して、以下のようにキーを入力します: (これは、AVAudioPlayer を拡張するために私が書いたカテゴリからカット アンド ペーストしたものです)

//  playFromConsts.h


define_key(AVAudioPlayer_key_player);
define_key(AVAudioPlayer_key_duration);
define_key(AVAudioPlayer_key_filename);
define_key(AVAudioPlayer_key_filepath);
define_key(AVAudioPlayer_key_fileurl);
define_key(AVAudioPlayer_key_urlString);
define_key(AVAudioPlayer_key_envelope);
define_key(AVAudioPlayer_key_startDate);
define_key(AVAudioPlayer_key_linkToPlayer);
define_key(AVAudioPlayer_key_linkFromPlayer);
define_key(AVAudioPlayer_key_linkToPlayerEnvelope);
define_key(AVAudioPlayer_key_linkFromPlayerEnvelope);
define_key(AVAudioPlayer_key_deviceStartTime);
define_key(AVAudioPlayer_key_currentVolume);
define_key(AVAudioPlayer_key_fadeFromVolume);
define_key(AVAudioPlayer_key_fadeToVolume);
define_key(AVAudioPlayer_key_fadeTime);
define_key(AVAudioPlayer_key_segueTime);

次に、normal.h ファイル (@interface、@protocol などが宣言されている場所) に、これらの 3 行を配置します (もちろん、ヘッダー ファイルを置き換えます)。

#define define_key(x) extern NSString * const x; 
#include "playFromConsts.h"
#undef define_key

最後に、「@interface .h」ファイルと対になっている .m ファイルに、次の 3 行を配置します。

#define define_key(x) NSString * const x = @#x; 
#include "playFromConsts.h"
#undef define_key

「#import」ではなく「#include」に注意してください。実際には、このファイルを複数回インクルードしたいのです。

これはすべての面倒な作業を行い、キーが NSString * const であることを保証します。

末尾; マクロに含まれているため、オプションですが、個人的には好みです。

于 2011-11-21T08:39:02.777 に答える
0

これは、ソフトウェアの設計によって異なります。ジョブ管理ソフトウェアがあり、その「デフォルト」の 1 つが、さまざまなアイテムを保存できるディレクトリのリストであるとします。

ジョブごとに、起動時にユーザーが優先する場所をロードするシングルトンである storagefile メンバーを持つことができます。

または、User Preferences というグローバル変数の Storagefile メンバーを持つこともできます。まだシングルトンである可能性がありますが、この場合はあまり重要ではありません。

私にとって、複雑なデフォルト (数十の異なるタイプのクラス) は、モデルにアクセス可能な独自の「スペース」に存在する必要があります。

ただし、ジョブのセットアップ方法に重要な設定がある場合があるため、それらの設定をジョブ オブジェクトに保存して、別のユーザーのアプリケーションで開いたときに意図したとおりに機能するようにする必要があります。

繰り返しますが、それはあなたのデザインに依存します。

于 2008-12-03T18:51:01.970 に答える