iOSアプリケーションのどこにグローバル定数を格納するのですか?


111

私のiOSアプリのほとんどのモデルはWebサーバーにクエリを送信します。サーバーのベースURLを格納する構成ファイルが欲しいのですが。次のようになります。

// production
// static NSString* const baseUrl = "http://website.com/"

// testing
static NSString* const baseUrl = "http://192.168.0.123/"

いずれかの行をコメント化することで、モデルが指すサーバーを即座に変更できます。私の質問は、iOSでグローバル定数を格納するためのベストプラクティスは何ですか?Androidプログラミングでは、この組み込みの文字列リソースファイルがあります。任意のアクティビティUIViewControllerに相当)では、次のようにしてこれらの文字列定数を取得できます。

String string = this.getString(R.string.someConstant);

iOS SDKに定数を格納するための同様の場所があるかどうか疑問に思っていました。そうでない場合、Objective-Cでそれを行うためのベストプラクティスは何ですか?

回答:


145

あなたもすることができます

#define kBaseURL @"http://192.168.0.123/"

「定数」ヘッダファイルに、言いますconstants.h。次に行う

#include "constants.h"

この定数が必要なすべてのファイルの先頭。

このように、次のように、コンパイラフラグに応じてサーバーを切り替えることができます。

#ifdef DEBUG
    #define kBaseURL @"http://192.168.0.123/"
#else
    #define kBaseURL @"http://myproductionserver.com/"
#endif

私はに基づいて変数を"constants.h"宣言するアプローチを使用していstaticます#ifdef VIEW_CONSTANTS ... #endif。したがって、私は1つのアプリケーション全体の定数ファイルを持っていますが、他のコードファイルはそれぞれ、定数ファイルの#define前に含めるさまざまな定数のセット#includeです(「定義されているが使用されていない」コンパイラの警告をすべて停止します)。

2
このソリューションで遭遇した2つの問題があります。まず、を使用#decalareすると、「無効な前処理指令が宣言されています」というコンパイルエラーが発生しました。#define代わりに変更しました。もう1つの問題は定数の使用です。で別の定数を作成したかったのstatic NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"]ですが、式でconstを作成するのは違法です。「initializer element is not constant」というエラーが表示されます。
ジョジョ

1
@Cyrille Androidは実践するのが本当に面白いです。iOSでは想像できなかった可能性があります。とにかく返信ありがとう
klefevre

8
可能な場合は#defineよりもconstを使用してください-コンパイル時のチェックが改善され、デバッグがより効率的になります。
オクルス2013年

2
@AnsonYao通常、私にそれが起こったとき、次のような#defineからセミコロンを削除するのを忘れました #define kBaseURL @"http://192.168.0.123/";
Gyfis

168

まあ、あなたはそれが関連するインターフェースにローカルな宣言を望んでいます-アプリ全体の定数ファイルは良いことではありません。

また、次のようにextern NSString* const使用するのではなく、単にシンボルを宣言することをお勧めします#define


SomeFile.h

extern NSString* const MONAppsBaseUrl;

SomeFile.m

#import "SomeFile.h"

#ifdef DEBUG
NSString* const MONAppsBaseUrl = @"http://192.168.0.123/";
#else
NSString* const MONAppsBaseUrl = @"http://website.com/";
#endif

C ++互換のExtern宣言の省略は別として、これは一般にAppleのObj-Cフレームワークで使用されるものです。

定数が1つのファイルまたは関数からのみ見えるようにする必要がある場合は、static NSString* const baseUrlin *.mが適切です。


26
なぜ承認された回答が#defineを支持するために40票を投じているのかわかりません-constは確かに優れています。
オクルス2013年

1
間違いなくconst NSStringは#defineよりも優れており、これが受け入れられる答えになるはずです。#defineは、定義された値が使用されるたびに新しい文字列を作成します。
jbat100 2013

1
@ jbat100新しい文字列を作成するとは思わない。コンパイラは、コードが同じ静的文字列を300,000回作成したかどうかを検出し、それを1回だけ作成すると思います。@"foo"と同じではありません[[NSString alloc] initWithCString:"foo"]
Abhi Beckert、2013

@AbhiBeckert私がjbatが作ろうとしていた点は、定数#defineを使用すると最終的に重複する可能性があることです(つまり、ポインタの等価性が失敗する可能性があります)-NSStringリテラル式が実行されるたびに一時的に生成されるわけではありません。
2013

1
#defineは悪い考えですが、複数のオブジェクトを作成するという彼のエラーを修正したいと思います。また、定数であってもポインタの等価性に依存することはできません。NSUserDefaultsなどからロードされる可能性があります。常にisEqual:を使用してください。
Abhi Beckert 2013

39

私がグローバル定数を定義する方法:


AppConstants.h

extern NSString* const kAppBaseURL;

AppConstants.m

#import "AppConstants.h"

#ifdef DEBUG
NSString* const kAppBaseURL = @"http://192.168.0.123/";
#else
NSString* const kAppBaseURL = @"http://website.com/";
#endif

次に、{$ APP} -Prefix.pchファイルで:

#ifdef __OBJC__
  #import <UIKit/UIKit.h>
  #import <Foundation/Foundation.h>
  #import "AppConstants.h"
#endif

問題が発生した場合は、まず[プリコンパイルプレフィックスヘッダー]オプションが[いいえ]に設定されていることを確認してください。



4

これを行う別の方法ははるかに簡単で、.pchプレフィックスファイルのように、すべてのファイルではなく、必要なファイルに含めるだけだと思います。

#ifndef Constants_h
#define Constants_h

//Some constants
static int const ZERO = 0;
static int const ONE = 1;
static int const TWO = 2;

#endif /* Constants_h */

その後、必要なヘッダーファイルにこのヘッダーファイルを含めます。これを、特定のクラスに含めたいヘッダーファイルに含めます。

#include "Constants.h"

私のテストでは、静的定数定数はデバッガー(Xcodeのlldb)では使用できません。 "error: use of undeclared identifier .."
jk7

3
  1. YOURPROJECT-Prefix.pchファイルでグローバル定数を定義しています。
  2. #define BASEURl @"http://myWebService.appspot.com/xyz/xx"
  3. 次に、プロジェクトの任意の場所でBASEURLを使用します。

    NSString *LOGIN_URL= [BASEURl stringByAppendingString:@"/users/login"];

更新:Xcode 6では、プロジェクトで作成されたデフォルトの.pchファイルが見つかりません。したがって、Xcode 6のPCHファイルを使用して、プロジェクトに.pchファイルを挿入してください。

アップデート:SWIFTの場合

  1. 新しいSwiftファイルを作成[クラスなしで空]と言う[AppGlobalMemebers]
  2. &すぐにメンバーを宣言/定義

    例:

    var STATUS_BAR_GREEN : UIColor  = UIColor(red: 106/255.0, green: 161/255.0, blue: 7/255.0, alpha: 1)  //
    1. クラスファイルでAppグローバルメンバーを定義する場合は、AppdelegateまたはSingletonクラスなどと言い、クラス定義の上に特定のメンバーを宣言します

2

グローバル宣言は興味深いものですが、私にとってコードに対する私の方法が大きく変わったのは、クラスのグローバルインスタンスを持つことでした。使い方を本当に理解するのに数日かかったので、ここで簡単にまとめました

クラスのグローバルインスタンス(必要に応じてプロジェクトごとに1または2)を使用して、コアデータアクセスまたはいくつかの取引ロジックを再グループ化します。

たとえば、すべてのレストランのテーブルを処理する中央オブジェクトが必要な場合は、起動時に作成するオブジェクトを作成します。このオブジェクトは、データベースアクセスを処理したり、保存する必要がない場合はメモリ内で処理したりできます。集中化されており、便利なインターフェースしか表示されません...!

それは素晴らしい助けであり、オブジェクト指向であり、すべてのものを同じ場所に集めるのに良い方法です

数行のコード:

@interface RestaurantManager : NSObject
    +(id) sharedInstance;
    -(void)registerForTable:(NSNumber *)tableId;
@end 

およびオブジェクトの実装:

@implementation RestaurantManager

+ (id) sharedInstance {
    static dispatch_once_t onceQueue;

    dispatch_once(&onceQueue, ^{
        sharedInstance = [[self alloc] init];
        NSLog(@"*** Shared instance initialisation ***");
    });
    return sharedInstance;
}

-(void)registerForTable:(NSNumber *)tableId {
}
@end

それを使用するためのそれは本当に簡単です:

[[RestaurantManager sharedInstance] registerForTable:[NsNumber numberWithInt:10]]


3
この設計パターンの技術名はシングルトンです。en.wikipedia.org/wiki/Singleton_pattern
Basil Bourque

sharedmanagerに静的データ(静的クラスではない)を保持することはお勧めできません。
Onder OZCAN 2016

1

受け入れられた回答には2つの弱点があります。まず、他の人が指摘したように、#defineように、デバッグが難しいextern NSString* const kBaseUrl。代わりに構造体を使用してください。次に、定数用の単一のファイルを定義します。IMO、これは間違っています。ほとんどのクラスはこれらの定数にアクセスする必要がないか、すべての定数にアクセスする必要がないため、すべての定数がそこで宣言されている場合、ファイルが肥大化する可能性があります。より良い解決策は、3つの異なるレイヤーで定数をモジュール化することです。

  1. システム層: SystemConstants.hまたはAppConstants.h 、システム内の任意のクラスからアクセスできるグローバルスコープの定数を記述します。ここでは、関連しない異なるクラスからアクセスする必要がある定数のみを宣言します。

  2. モジュール/サブシステムレイヤー: ModuleNameConstants.hは、モジュール/サブシステム内の、関連するクラスのセットに典型的な定数のセットを記述します。

  3. クラス層:定数はクラスに常駐し、クラスによってのみ使用されます。

質問に関連しているのは1,2だけです。


0

以前に使用したアプローチは、ファイルを作成Settings.plistNSUserDefaults、起動時にを使用してロードすることregisterDefaults:です。その後、次の方法でコンテンツにアクセスできます。

// Assuming you've defined some constant kMySettingKey.
[[NSUserDefaults standardUserDefaults] objectForKey:kMySettingKey];

私はAndroid開発をまだ行っていませんが、これはあなたが説明した文字列リソースファイルに類似しているようです。唯一の欠点は、プリプロセッサを使用して設定を切り替えることができないことです(例:DEBUGモード)。ただし、別のファイルをロードすることもできます。

NSUserDefaults ドキュメンテーション。


9
必要なのが定数である場合、それは少しやりすぎではありませんか?また、変更可能なファイルに配置するのはなぜですか?(特に、マスターサーバーのIPと同じくらい重要で、それがないとアプリが機能しません)。
シリル、2011

私は、このアプローチはいくつかの利点、設定が正しい形式(で返されることが最も重要なビーイング持っていることを感じNSStringNSNumberなど)。もちろん、#definesをラップして同じことを行うこともできますが、編集するのはそれほど簡単ではありません。plist編集インターフェースでは、あまりにも、いいです。:)暗号化キーなどの極秘情報をそこに入れてはいけないことには同意しますが、本来はすべきではない場所を探し回っているユーザーについてはあまり気にしていません。ユーザーがアプリを壊したとしても、それは自分の責任です。 。
Chris Doble、2011

1
確かに、私はあなたの主張に同意します。あなたが言う#defineように、正しいタイプを返すようにs をラップしますが、このような定数ファイルを編集することに慣れています。これは、Pascalを学んだ時代から、このようなグローバル定数を別の定数ファイルに入れることを常に学んでいたためです。古い286について:)そして、いたるところをつついているユーザーに関しては、私も同意しますが、それは彼らの責任です。本当にそれは好みの問題です。
シリル、2011

@Chris Doble:いいえ、AndroidのリソースファイルはNSUserDefaultsに似ていません。SharedPreferencesおよびPreferencesは、NSUserDefaultsに相当するAndroidです(ただし、NSUserDefaultsよりも強力です)。Androidのリソースは、ローカリゼーションやその他の多くの用途のために、ロジックをコンテンツから分離することを目的としています。
mrd

0

あなたはそれを次のように使うことができます

#define MAX_HEIGHT 12.5

0

私は思い構成オブジェクト使用から初期化しますplist。無関係な外部のもので他のクラスを悩ませるのはなぜですか?

eppz!settignsこのため、私はsoley を作成しました。からデフォルト値を組み込む方法については、NSUserDefaultsに保存する高度でシンプルな方法の記事を参照してくださいplist

ここに画像の説明を入力してください


そうだね。Wordpressのは、何とか解決していない...:/ ...とにかく、この直接リンクして行くblog.eppz.eu/?p=926:D
ジェリBorbás
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.