Cocoaアプリケーションを開発していNSString
て、設定のキー名を格納する方法として定数sを使用しています。
必要に応じてキーを簡単に変更できるため、これは良い考えだと理解しています。
さらに、それは全体を「ロジックからデータを分離する」という概念です。
とにかく、これらの定数をアプリケーション全体で一度定義する良い方法はありますか?
簡単でインテリジェントな方法があると確信していますが、現在のところ、私のクラスは使用するクラスを再定義しているだけです。
Cocoaアプリケーションを開発していNSString
て、設定のキー名を格納する方法として定数sを使用しています。
必要に応じてキーを簡単に変更できるため、これは良い考えだと理解しています。
さらに、それは全体を「ロジックからデータを分離する」という概念です。
とにかく、これらの定数をアプリケーション全体で一度定義する良い方法はありますか?
簡単でインテリジェントな方法があると確信していますが、現在のところ、私のクラスは使用するクラスを再定義しているだけです。
回答:
次のようなヘッダーファイルを作成する必要があります
// Constants.h
FOUNDATION_EXPORT NSString *const MyFirstConstant;
FOUNDATION_EXPORT NSString *const MySecondConstant;
//etc.
(コードがC / C ++混合環境または他のプラットフォームで使用されない場合は、extern
代わりに使用できFOUNDATION_EXPORT
ます)
このファイルは、定数を使用する各ファイル、またはプロジェクトのプリコンパイル済みヘッダーに含めることができます。
これらの定数を.mファイルで次のように定義します
// Constants.m
NSString *const MyFirstConstant = @"FirstConstant";
NSString *const MySecondConstant = @"SecondConstant";
Constants.mをアプリケーション/フレームワークのターゲットに追加して、最終製品にリンクされるようにする必要があります。
#define
'd 定数の代わりに文字列定数を使用する利点はstringInstance == MyFirstConstant
、文字列比較([stringInstance isEqualToString:MyFirstConstant]
)よりも高速なポインター比較()を使用して等価性をテストできることです(そして、読みやすく、IMO)。
NSString
プロパティを定義する多くのクラスを見てきました。そのため、それらは定数の別のインスタンスを保持している可能性があり(そうする必要があります)、直接メモリアドレスの比較は失敗します。また、の合理的に最適化された実装では、文字比較の要点に入る前にポインタの等価性をチェックすると思います。copy
retain
NSString*
-isEqualToString:
最も簡単な方法:
// Prefs.h
#define PREFS_MY_CONSTANT @"prefs_my_constant"
より良い方法:
// Prefs.h
extern NSString * const PREFS_MY_CONSTANT;
// Prefs.m
NSString * const PREFS_MY_CONSTANT = @"prefs_my_constant";
2番目の利点の1つは、定数の値を変更してもプログラム全体が再構築されないことです。
extern NSString const * const MyConstant
、つまり、単なる定数ポインターではなく、定数オブジェクトへの定数ポインターにするのですか?
言及すべきことが1つあります。非グローバル定数が必要な場合は、static
キーワードを使用する必要があります。
例
// In your *.m file
static NSString * const kNSStringConst = @"const value";
static
キーワードがあるため、このconstはファイルの外からは見えません。
@QuinnTaylorによるマイナー修正:静的変数はコンパイルユニット内に表示されます。通常、これは(この例のように)単一の.mファイルですが、コンパイル後にリンカーエラーが発生するため、他の場所に含まれているヘッダーで宣言するとかみつく可能性があります
受け入れられた(そして正しい)答えは、「この[Constants.h]ファイルをプロジェクトのプリコンパイル済みヘッダーに含めることができる」と述べています。
初心者として、これ以上の説明なしでこれを行うのは困難でした-方法は次のとおりです:YourAppNameHere-Prefix.pchファイル(これはXcodeのプリコンパイル済みヘッダーのデフォルト名です)で、#ifdef __OBJC__
ブロック内に Constants.h をインポートします。
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "Constants.h"
#endif
また、Constants.hファイルとConstants.mファイルには、承認された回答に記載されている内容以外は絶対に何も含めないでください。(インターフェースや実装はありません)。
私は通常、Barry WarkとRahul Guptaが投稿した方法を使用しています。
ただし、.hファイルと.mファイルの両方で同じ単語を繰り返すのは好きではありません。次の例では、両方のファイルで行がほとんど同じであることに注意してください。
// file.h
extern NSString* const MyConst;
//file.m
NSString* const MyConst = @"Lorem ipsum";
したがって、私がやりたいことは、いくつかのCプリプロセッサー機構を使用することです。例を通して説明しましょう。
マクロを定義するヘッダーファイルがありますSTR_CONST(name, value)
。
// StringConsts.h
#ifdef SYNTHESIZE_CONSTS
# define STR_CONST(name, value) NSString* const name = @ value
#else
# define STR_CONST(name, value) extern NSString* const name
#endif
定数を定義したい.h / .mペアで、次のようにします。
// myfile.h
#import <StringConsts.h>
STR_CONST(MyConst, "Lorem Ipsum");
STR_CONST(MyOtherConst, "Hello world");
// myfile.m
#define SYNTHESIZE_CONSTS
#import "myfile.h"
et voila、私は.hファイルにのみ定数に関するすべての情報を持っています。
私自身は、次のような設定に使用される定数NSStringの宣言専用のヘッダーを持っています。
extern NSString * const PPRememberMusicList;
extern NSString * const PPLoadMusicAtListLoad;
extern NSString * const PPAfterPlayingMusic;
extern NSString * const PPGotoStartupAfterPlaying;
次に、それらを添付の.mファイルで宣言します。
NSString * const PPRememberMusicList = @"Remember Music List";
NSString * const PPLoadMusicAtListLoad = @"Load music when loading list";
NSString * const PPAfterPlayingMusic = @"After playing music";
NSString * const PPGotoStartupAfterPlaying = @"Go to startup pos. after playing";
このアプローチは私に役立ちました。
編集:文字列が複数のファイルで使用されている場合、これが最もよく機能することに注意してください。1つのファイルだけが使用する場合#define kNSStringConstant @"Constant NSString"
は、文字列を使用する.mファイルで行うことができます。
@Krizzの提案をわずかに変更したため、定数ヘッダーファイルがPCHに含まれる場合でも正常に機能しますが、これはかなり正常です。オリジナルはPCHにインポートされるため、.m
ファイルに再ロードされず、シンボルが表示されず、リンカーは不満を感じます。
ただし、次の変更により機能します。少し複雑ですが、機能します。
あなたは必要があります3つのファイル、.h
定数の定義を持っているファイル、.h
ファイルと.m
私が使用します、ファイルをConstantList.h
、Constants.h
そしてConstants.m
それぞれ。の内容Constants.h
は単純です:
// Constants.h
#define STR_CONST(name, value) extern NSString* const name
#include "ConstantList.h"
そしてConstants.m
ファイルは次のようになります:
// Constants.m
#ifdef STR_CONST
#undef STR_CONST
#endif
#define STR_CONST(name, value) NSString* const name = @ value
#include "ConstantList.h"
最後に、ConstantList.h
ファイルには実際の宣言が含まれており、それがすべてです。
// ConstantList.h
STR_CONST(kMyConstant, "Value");
…
注意すべき点がいくつかあります。
マクロを使用した後、マクロを.m
ファイルに再定義する必要がありました。 #undef
これを適切に機能させ、以前にプリコンパイルされた値がコンパイラに表示されないようにするために、#include
代わりにを使用する必要もありました#import
。
これには、値が変更されるたびにPCH(およびおそらくプロジェクト全体)の再コンパイルが必要になります。これは、通常どおりに値が分離(および複製)されている場合には当てはまりません。
それが誰かに役立つことを願っています。
extern
上記をに置き換えた場合も、まったく同じですFOUNDATION_EXPORT
。
Abizerが言ったように、それをPCHファイルに入れることができます。それほど汚くないもう1つの方法は、すべてのキーのインクルードファイルを作成し、キーを使用しているファイルにそのファイルをインクルードするか、PCHにインクルードすることです。それらを独自のインクルードファイルに含めると、少なくともこれらすべての定数を検索および定義するための1つの場所が提供されます。
クラスメソッドを使用してみてください。
+(NSString*)theMainTitle
{
return @"Hello World";
}
時々使います。
isEqualToString:
、比較に使用する必要があります。実行時の追加コスト。定数が必要な場合は、定数を作成します。
名前空間定数が必要な場合は、構造体、金曜日のQ&A 2011-08-19:名前空間定数と関数を活用できます。
// in the header
extern const struct MANotifyingArrayNotificationsStruct
{
NSString *didAddObject;
NSString *didChangeObject;
NSString *didRemoveObject;
} MANotifyingArrayNotifications;
// in the implementation
const struct MANotifyingArrayNotificationsStruct MANotifyingArrayNotifications = {
.didAddObject = @"didAddObject",
.didChangeObject = @"didChangeObject",
.didRemoveObject = @"didRemoveObject"
};
__unsafe_unretained
修飾子を付ける必要があります。
シングルトンクラスを使用しているので、必要に応じて、クラスをモックして定数を変更できます。定数クラスは次のようになります。
#import <Foundation/Foundation.h>
@interface iCode_Framework : NSObject
@property (readonly, nonatomic) unsigned int iBufCapacity;
@property (readonly, nonatomic) unsigned int iPort;
@property (readonly, nonatomic) NSString * urlStr;
@end
#import "iCode_Framework.h"
static iCode_Framework * instance;
@implementation iCode_Framework
@dynamic iBufCapacity;
@dynamic iPort;
@dynamic urlStr;
- (unsigned int)iBufCapacity
{
return 1024u;
};
- (unsigned int)iPort
{
return 1978u;
};
- (NSString *)urlStr
{
return @"localhost";
};
+ (void)initialize
{
if (!instance) {
instance = [[super allocWithZone:NULL] init];
}
}
+ (id)allocWithZone:(NSZone * const)notUsed
{
return instance;
}
@end
そしてそれは次のように使用されます(定数cの省略形の使用に注意してください- [[Constants alloc] init]
毎回入力する手間が省けます):
#import "iCode_FrameworkTests.h"
#import "iCode_Framework.h"
static iCode_Framework * c; // Shorthand
@implementation iCode_FrameworkTests
+ (void)initialize
{
c = [[iCode_Framework alloc] init]; // Used like normal class; easy to mock!
}
- (void)testSingleton
{
STAssertNotNil(c, nil);
STAssertEqualObjects(c, [iCode_Framework alloc], nil);
STAssertEquals(c.iBufCapacity, 1024u, nil);
}
@end