Cocoa/Objective-C 中的全局变量?
-
19-08-2019 - |
题
根据 Mac OS X 的 Cocoa 编程,第三版, 第 202 页(第 13 章):
您将注册、阅读和 在多个类中设置默认值 您的应用程序。要确保 你总是使用同一个名字,你 应该在 单个文件,然后简单地 #import 该文件复制到您 使用名称。有几种方法 来执行此操作。例如,您可以使用 C 预处理器的 #define 命令, 但大多数 Cocoa 程序员使用 global 用于此目的的变量。
这真的是正确的最佳实践吗?全局变量?这对我来说似乎很疯狂——与我所学到的一切背道而驰。
更好的设计是定义这些定义的简单单例类吗?或者说走出去真的是正确的最佳实践吗?鉴于许多人认为单身人士是穿着漂亮衣服的全球人,还有比这两种模式更好的模式吗?
解决方案
全局变量或单身将在这里完成同样的事情。既可以用来把可可“关键”的名字,如果它拼错成一个编译器错误,不会抛出一个编译器错误。这是最主要的目的。全局变量,虽然看到它需要较少的输入更容易一些。
代替这样做的:
[myArray setObject:theObject forKey:MyGlobalVariableKeyName];
您必须做的线沿线的东西:
[myArray setObject:theObject
forKey:[[MySingletonVariableClass getInstance] myVariableKeyName];
全局变量是相同的效果基本上更少的打字。
其他提示
只要是明确的,推荐是创建的一成不变的全局变量,而不是在线字符串常量(难以重构,没有编译时检查)或#定义(没有编译时检查)。这里是你会如何做?
在MyConstants.h:
extern NSString * const MyStringConstant;
在MyConstants.m:
NSString * const MyStringConstant = @"MyString";
然后在任何其他.m文件:
#import "MyConstants.h"
...
[someObject someMethodTakingAString:MyStringConstant];
...
此方式,您将获得编译时检查你有没有拼写错误的字符串常量,你可以在你的比较常量检查指针平等,而不是字符串相等[1],和调试比较容易,因为常数有一个运行时的字符串值。
[1]在此使用时,实质上是使用指针值作为常数。它碰巧的是,这些特定的整数也指向可以在调试器中使用的字符串
将其称为全局变量在技术上是正确的,但具有误导性。
它是一个全局常量——作用域是全局的,但是是常量,因此从全局变量不好的意义上来说,它还不错。
展示如何全球化 常量 常见、安全且数量众多,请考虑以下全局常量的示例:
- 课程中的每个课程
- 每个#define
- 每个枚举
- 几乎 Cocoa 声明的每个名称(不包括罕见的全局变量,例如
NSApp
).
您唯一应该担心全局常量的时候是它们的名称太通用(它们可能会污染全局命名空间)。因此,不要使用可能与任何内容冲突的名称(始终使用前缀并始终使名称特定于任务,例如 NSKeyValueObservingOptionNew
).
恒全局是可接受的我。如果你硬编码字符串,这是同样的事情,只是由编译器隐藏。我会避免可变全局像瘟疫。
记住,苹果本身使用相同的技术。很多我想像的要定义常量实际上常数。你会得到链接错误,如果头是可达但框架是没有的。
建筑物上@Barry沃克的和@马特加拉格尔的优良的答案,以及我的初始响应(参见此答案的一端)有一第三种方法,那就是使用宏/包括组合,可确保只键入变量名一次,因此,被包括在两个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”整个一个例子的缘故。
在另一文件I可能会使用 “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);
然后在您normal.h文件(其中,你的@interface,@protocol等被声明)放置这些3行(代当然头文件)
#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”而不是“#进口” - 我们其实要包含这个文件不止一次
。这将尽一切肮脏的工作,并确保密钥的NSString * const的。
尾随;是可选的,因为它包含在宏,但是我个人更喜欢它。
因此毕竟。我想出了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);
这要看你的软件的设计。假设你有一份工作管理软件和您的“违约”的一个是在各个项目可以保存的目录清单。
有关每个工作可以有一个storagefile构件,其在启动时加载了用户优选位置处的单例。
或者你可以有一个叫做用户首选项的全局变量的Storagefile成员。仍然可能是一个单身,但并不在这种情况下,真正的问题。
对于我来说复杂的默认值(几十种不同类型的类)应该驻留在自己的“空间”访问模型。
然而,有可能是对如何对作业设置,以便那些偏好需要被存储在工作对象重要的喜好,所以当你在另一个用户的应用程序中打开它,它按预期工作。
此外它取决于你的设计。