Cocoa / Objective-C中的全局variables?

根据Cocoa Programming for Mac OS X,第3版, 202页(第13章):

您将在您的应用程序的几个类中注册,阅读和设置默认值。 为了确保您始终使用相同的名称,您应该将这些string声明为单个文件,然后将该文件简单地导入到您使用名称的任何文件中。 有几种方法可以做到这一点。 例如,你可以使用C预处理器的#define命令,但是大多数Cocoa程序员为此使用全局variables。

这真的是最好的做法吗? 全局variables? 这对我来说似乎是疯了 – 对抗我所教过的一切。

更好的devise是一个简单的单一类与这些定义? 还是真的是走出去的正确的最佳做法? 考虑到许多人认为单身男人穿着漂亮的连衣裙变成全能的,是不是有更好的模式呢?

全局variables或单例将在这里完成相同的事情。 两者都可以用来打开Cocoa中的'键'名字,如果拼写错误的话就不会抛出编译错误。 这是主要目的。 全局variables虽然看起来稍微简单一些,因为它需要更less的input。

而不是这样做:

[myArray setObject:theObject forKey:MyGlobalVariableKeyName]; 

你必须做一些事情:

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

全局variables本质上是less打字相同的效果。

为了清楚起见,build议创build不可变的全局variables,而不是内联的string常量(难以重构,无编译时检查)或#define(无编译时检查)。 你可能会这么做

在MyConstants.h中:

 extern NSString * const MyStringConstant; 

在MyConstants.m中:

 NSString * const MyStringConstant = @"MyString"; 

然后在任何其他.m文件中:

 #import "MyConstants.h" ... [someObject someMethodTakingAString:MyStringConstant]; ... 

通过这种方式,您可以在编译时检查您是否拼错了一个string常量,您可以在比较常量时检查指针是否相等,而不是string相等[1],debugging更容易,因为常量有运行时间string值。

[1]在这个使用中,你基本上使用指针值作为常量。 恰好这些特定的整数也指向了可以在debugging器中使用的string

把它称为一个全局variables在技术上是正确的,但是具有误导性。

它是一个全球常数 – 全球范围内不变,因此在全球variables不好的情况下并不坏。

为了说明全局常量是如何常见,安全和众多的,请考虑这些全局常量的例子:

  • 你程序中的每一堂课
  • 每个#定义
  • 每个枚举
  • 几乎所有cocoa声明的名字(不包括像NSApp这样的罕见的全局variables)。

你唯一需要担心的是全局常量是他们的名字太泛化了(他们可能会污染全局名字空间)。 所以不要使用可能与任何东西冲突的名称(总是使用前缀,并始终使名称任务特定,如NSKeyValueObservingOptionNew )。

在编译时设置并永不改变的恒定全局variables对我来说是可以接受的。 如果你硬编码一个string,这是一样的事情,只是由编译器隐藏。 我将避免像鼠疫这样的可变全局variables。

请记住,苹果公司本身使用相同的技术。 我期望定义的许多常量实际上是常量。 如果标题可以访问但是框架不可用,则会出现链接错误。

基于@Barry Wark和@Matt Gallagher的出色答案,以及我最初的回答(请参阅本答案的结尾),还有第三种方法,那就是使用一个macros/包含组合,以确保您只input一次variables名称,因此它同时包含在.h和.m文件中。

<编辑>

“总有另一种方式……”

在思考如何使它更简单,不涉及额外的头文件,这是一个更简洁的方法使用嵌套的macros。

在.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”的名称更改为唯一的,我build议将它与您正在定义的键的前缀相同 – 为了我已经使用“ myKey“。

在另一个文件中,我可能会使用“myOtherKeyDefineKeys”。

也不要乱用defineKeysIn_h_File和defineKeysIn_m_Filemacros,否则会得到警告,定义已经改变。

<END EDIT>

原始答案,仍然有效,但没有细化

首先,制作一个vanilla.h文件,并删除默认的#ifdef等,并input您的密钥,如下所示:(这是从我写的扩展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行(当然你的头文件replace)

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

最后在您的.m文件中,与您的“@interface .h”文件配对,放置这3行:

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

注意“#include”而不是“#import” – 我们实际上不想多次包含这个文件。

这将做所有的肮脏的工作,并确保密钥是NSString *常量。

尾随; 是可选的,因为它包含在macros中,但是我个人更喜欢它。

毕竟。 我想出了3个文件。

Constants.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" 

Constants.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); 

这取决于你的软件的devise。 假设你有一个作业pipe理软件,你的“默认”是一个可以保存各种项目的目录列表。

对于每个作业,您可以拥有一个存储文件成员,它是一个在启动时加载用户首选位置的单例。

或者你可以有一个名为User Preferences的全局variables的Storagefile成员。 仍然可以是一个单身人士,但在这种情况下并不重要。

对于我来说,复杂的默认值(几十种不同的类)应该存在于模型可以访问的“空间”中。

但是,可能有一些首选项对于如何设置作业很重要,因此这些首选项需要存储在作业对象中,因此当您在另一个用户的应用程序中打开它时,它将按预期工作。

这又取决于你的devise。