在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编程中,我们具有此内置字符串资源文件。在任何Activity中(等效于UIViewController),我们都可以使用以下方法检索这些字符串常量:

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

我想知道iOS SDK是否有类似的位置来存储常量。如果不是,那么Objective-C的最佳实践是什么?

Answers:


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。因此,我有一个在应用程序范围内的常量文件,但是#define#include对常量文件进行操作之前,我的每个其他代码文件都包含不同的常量集(停止所有“已定义但未使用”的编译器警告)。

2
此解决方案遇到两个问题。首先,使用时#decalare,出现编译错误,提示“ 无效的预处理指令声明 ”。所以我改成了#define。另一个问题是使用常数。我想用创建另一个常量static NSString* const fullUrl = [NSString stringWithFormat:@"%@%@", kbaseUrl, @"script.php"],但是用表达式创建const显然是非法的。我收到错误“ 初始化元素不是常量 ”。
JoJo

1
@Cyrille Android的练习非常有趣,在iOS上有您无法想象的可能性!无论如何,谢谢您的答复
klefevre

8
在可能的情况下,最好使用const而不是#define -您可以获得更好的编译时检查,并且调试效果更好。
occulus

2
@AnsonYao通常当我发生这种情况时,我忘记从#define中删除分号,例如 #define kBaseURL @"http://192.168.0.123/";
Gyfis 2015年

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框架中看到这种用法。

如果只需要一个文件或函数可见该常量,那么static NSString* const baseUrl对您而言*.m就很好了。


26
不确定为什么接受的答案有40票赞成#define投票-const确实更好。
occulus

1
绝对const NSString比#define更好,这应该是公认的答案。每次使用定义的值时,#define都会创建一个新字符串。
jbat100

1
@ jbat100我不认为它会创建新的字符串。我认为编译器会检测您的代码是否创建相同的静态字符串300,000次,并且只会创建一次。@"foo"[[NSString alloc] initWithCString:"foo"]
Abhi Beckert 2013年

@AbhiBeckert我认为jbat试图提出的要点是,使用完该常量#define可能会导致常量的重复(即指针相等性可能会失败)-不是NSString文字表达式每次执行时都会产生一个临时值。
贾斯汀

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

如果遇到任何问题,请首先确保将Precompile Prefix Header选项设置为NO。


5

您还可以像这样连接字符串常量:

  #define kBaseURL @"http://myServer.com"
  #define kFullURL kBaseURL @"/api/request"

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"

在我的测试中,静态const常量在调试器(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. 如果要在任何类文件中定义应用程序全局成员(例如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
该设计模式的技术名称为Singleton。en.wikipedia.org/wiki/Singleton_pattern
罗勒·布尔克

在sharedmanager中保留静态数据(不是静态类)不是一个好主意。
OnZ OZCAN '16

1

接受的答案有2个缺点。首先,正如其他人指出的那样#define,使用起来较难调试,请改用extern NSString* const kBaseUrl结构。其次,它为常量定义了一个文件。IMO,这是错误的,因为大多数类不需要访问这些常量,也不需要访问所有常量,而且如果在其中声明了所有常量,文件可能会变得肿。更好的解决方案是在3个不同的层上对常数进行模块化:

  1. 系统层:SystemConstants.hAppConstants.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一样重要时,如果没有它,您的应用程序将无法运行)。
Cyrille

我觉得这种方法有几个好处,其中最显著的存在,你的设置是正确的格式(返回NSStringNSNumber等)。当然可以#define s以完成相同的操作,但是那样编辑起来就不那么容易了。该plist编辑界面很不错了。:)虽然我同意您不应在其中放置诸如加密密钥之类的超级机密信息,但我不太担心用户在不应该使用的地方闲逛-如果他们破坏了应用程序,那是他们自己的错。
克里斯·多布尔

1
当然,我同意你的论点。如您所说,我包装#defines返回正确的类型,但是我习惯于编辑这样的常量文件,因为我从学会Pascal的那一天开始就一直学会将这样的全局常量放在单独的常量文件中。在旧的286上:)至于到处乱逛的用户,我也同意,这是他们的错。真的,这只是口味问题。
西里尔

@Chris Doble:不,Android中的资源文件与NSUserDefaults不相似。Android上的SharedPreferences和Preferences首选项等效于N​​SUserDefaults(尽管比NSUserDefaults更强大)。Android中的资源旨在将逻辑与内容(例如本地化)以及许多其他用途分开。
2014年

0

对于一个数字,你可以像

#define MAX_HEIGHT 12.5

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.