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

Cocoa/Objective-C中的全局变量?

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

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

    您将进行注册、阅读和 在中的几个类中设置默认值 您的应用程序。为了确保 你总是用同一个名字,你 应该在a中声明这些字符串 单个文件,然后简单地导入 将该文件转换为您所在的任何文件 使用这些名字。有几种方法 为了做到这一点。例如,您可以使用 C预处理器#define命令, 但大多数Cocoa程序员使用全局 为此目的的变量。

    这真的是正确的最佳实践吗?全局变量?在我看来,这似乎很疯狂,与我所学的一切背道而驰。

    一个更好的设计是一个定义了这些的简单Singleton类吗?或者,走向全球真的是正确的最佳实践吗?考虑到许多人认为单身汉是穿着漂亮衣服的全球人,还有比这两种模式更好的模式吗?

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

    需要明确的是,建议创建 不变的 全局变量而不是内联字符串常量(难以重构,没有编译时检查)或#defines(没有编译时检测)。你可以这样做。。。

    在MyConstants.h中:

    extern NSString * const MyStringConstant;
    

    在MyConstants.m中:

    NSString * const MyStringConstant = @"MyString";
    

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

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

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

    [1] 在这种使用中,您基本上是将指针值用作常量。恰好,这些特定的整数也指向可以在调试器中使用的字符串

        2
  •  63
  •   gcamp    13 年前

    全局变量或单例在这里也会完成同样的事情。两者都可以用来转换Cocoa中的“关键”名称,如果拼写错误,不会引发编译器错误。这就是主要目的。全局变量更容易查看,因为它需要更少的键入。

    与其这样做:

    [myArray setObject:theObject forKey:MyGlobalVariableKeyName];
    

    你必须采取以下行动:

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

    为了达到相同的效果,全局变量基本上不需要打字。

        3
  •  18
  •   mattjgalloway    13 年前

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

    它是一个全局常量——范围是全局的,但也是常量,因此从全局变量是坏的意义上讲,它还不错。

    展示全球 常数 常见、安全且数量众多,请考虑以下全局常数示例:

    • 你项目中的每一堂课
    • 每个#define
    • 每个枚举
    • Cocoa声明的几乎所有名称(不包括罕见的全局变量,如 NSApp ).

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

        4
  •  3
  •   user28709    16 年前

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

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

        5
  •  1
  •   unsynchronized    13 年前

    基于@Barry Wark和@Matt Gallagher的出色回答,以及我最初的回应(见本答案末尾),还有第三种方法,即使用macro/include组合,确保只键入一次变量名,因此它同时包含在.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_File和defineKeysIn_m_File宏,否则你会收到定义已更改的警告。

    <结束编辑>

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

    首先,制作一个vanilla.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”而不是“#import”-我们确实希望多次包含此文件。

    这将完成所有脏活累活,并确保键是NSString*const。

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

        6
  •  1
  •   Denis Mikhaylov    13 年前

    所以,毕竟。我找到了三个文件。

    常数。英语字母表的第8个字母

    #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"
    

    常数。男性

    #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"
    

    警察Defs。英语字母表的第8个字母

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

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

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

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

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

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

    这又取决于你的设计。