代码之家  ›  专栏  ›  技术社区  ›  John Rudy

cocoa/objective-c中的全局变量?

  •  19
  • John Rudy  · 技术社区  · 16 年前

    根据 Mac OS X的Cocoa编程,第3版, 第202页(第13章):

    您将进行注册、阅读和 在中的几个类中设置默认值 你的申请。以确保 你总是用同样的名字,你 应该在 单个文件,然后简单地导入 把那个文件放到 使用这些名字。有几种方法 这样做。例如,您可以使用 c预处理器define命令, 但是大多数可可程序员使用全局 用于此目的的变量。

    这真的是正确的最佳实践吗?全局变量?在我看来,这与我所学过的每一件事都背道而驰。

    一个更好的设计是一个定义了这些的简单的单例类吗?或者它真的是走向全球的最佳实践吗?有没有比这两种模式更好的模式,考虑到许多人认为独生子女是穿着漂亮衣服的全球人?

    7 回复  |  直到 12 年前
        1
  •  18
  •   Grant Limberg    16 年前

    全局变量或单例变量将在这里完成相同的事情。两者都可以用来在cocoa中转换“key”名称,如果将其拼写错误为编译器错误,则不会引发编译器错误。这是主要目的。全局变量更容易看到,因为它需要更少的输入。

    而不是这样做:

    [myArray setObject:theObject forKey:MyGlobalVariableKeyName];
    

    你必须按照以下原则做一些事情:

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

    对于相同的效果,全局变量的类型基本上更少。

        2
  •  63
  •   gcamp    13 年前

    为了清楚起见,建议是 不变的 全局变量而不是内联字符串常量(难以重构且无编译时检查)或定义(无编译时检查)。你可以这样做…

    在myinstants.h中:

    extern NSString * const MyStringConstant;
    

    在myinstants.m中:

    NSString * const MyStringConstant = @"MyString";
    

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

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

    这样,就可以在编译时检查字符串常量是否拼写错误,在比较常量时可以检查指针是否相等,而不是字符串是否相等[1],而且调试更容易,因为常量具有运行时字符串值。

    [1]在这种用法中,您基本上使用指针值作为常量。正因为如此,那些特定的整数也指向可以在调试器中使用的字符串

        3
  •  18
  •   mattjgalloway    12 年前

    将其称为全局变量在技术上是正确的,但有误导性。

    它是一个全局常量——在范围内是全局的,但在全局变量不好的意义上是不变的,因此也不坏。

    显示全局 常量 是常见的、安全的和众多的,请考虑这些全局常量的示例:

    • 你课程中的每堂课
    • 每一个定义
    • 每一个枚举
    • 几乎所有由cocoa声明的名称(不包括诸如 NSApp )

    唯一需要担心的是全局常量的名称过于通用(它们可能会污染全局命名空间)。因此,不要使用可能与任何内容相冲突的名称(始终使用前缀,并始终使名称与任务相关,如 NSKeyValueObservingOptionNew )

        4
  •  3
  •   user28709    16 年前

    在编译时设置且从不更改的常量全局对我来说是可以接受的。如果硬编码一个字符串,它是相同的,只是被编译器隐藏了。我会避免像瘟疫这样的易变的地球。

    记住,苹果本身也使用同样的技术。我期望定义的许多常量实际上都是常量。如果头是可访问的,但框架是不可访问的,则会出现链接错误。

        5
  •  1
  •   unsynchronized    13 年前

    基于@barry wark'和@matt gallagher的优秀答案,以及我的初始响应(参见本答案的结尾),还有第三种方法,即使用宏/包含组合,确保只键入一次变量名,因此它同时包含在.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_文件和definekeysin_m_文件宏,否则您将收到一条警告,说明定义已更改。

    <结束编辑&

    原始答案,仍然有效,但未作改进

    首先,创建一个普通的.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);
    

    然后在您的普通.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
    

    注意“include”和“not”import”-我们实际上希望多次包含此文件。

    这将完成所有脏的工作,并确保密钥是nsstring*const。

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

        6
  •  1
  •   Denis Mikhaylov    12 年前

    所以毕竟。我想出了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

    def_key(kStringConstant);
    def_int(kIntConstant, 313373);
    def_type(float, kFloatConstant, 313373.0f);
    
        7
  •  0
  •   RS Conley    16 年前

    这取决于软件的设计。假设您有一个作业管理软件,其中一个“默认值”是可以保存各种项目的目录列表。

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

    或者您可以拥有一个称为用户首选项的全局变量的storagefile成员。仍然可能是单身,但在这种情况下并不重要。

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

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

    这又取决于你的设计。