Objective-C中本地常量的最佳实践

10

我看到很多Objective-C代码只是定义它需要的本地常量,然后继续进行。问题在于,据我所知,#define没有作用域限制。其中许多都在苹果自己的示例代码中。例如,在TableViewSuite示例5中,TimeZoneView.m文件中的drawRect函数包含以下块:

#define LEFT_COLUMN_OFFSET 10
#define LEFT_COLUMN_WIDTH 130

#define MIDDLE_COLUMN_OFFSET 140
#define MIDDLE_COLUMN_WIDTH 110

#define RIGHT_COLUMN_OFFSET 270

#define UPPER_ROW_TOP 8
#define LOWER_ROW_TOP 34

#define MAIN_FONT_SIZE 18
#define MIN_MAIN_FONT_SIZE 16
#define SECONDARY_FONT_SIZE 12
#define MIN_SECONDARY_FONT_SIZE 10

我不明白为什么这不是非常危险的,有没有什么原因? 至少,在函数结尾处,我们应该取消定义这些常量吧?

这就是我的问题:

在需要使用的文件中定义所需内容并在结尾处取消定义是否更好?还是您认为仅使用静态常量更好?使用静态常量是否会有性能损失,或者编译器能够像处理#define一样高效地处理它们?

2个回答

13
在实现文件(.m)中的 #defines 是按定义作用域于它们所在的文件,因为没有其他人 #include 一个 .m 文件。(您需要在常见的头文件中仔细考虑这个作用域问题,在那里,您提到的这个作用域问题是真实存在的,应该根据 SO_QUESTION_2345197_NAMESPACE_YOUR_CONSTANTS_APPROPRIATELY 命名空间适当地使用您的常量。)
对于实现文件中的本地常量(这似乎是您所问的),#define 更高效地编译,但是调试时无法获取符号。本地常量具有这种优点,并且在某些情况下(字符串常量?也许?取决于),防止二进制数据中常量数据的重复,尽管目前世界上,大小和编译效率(以及运行时效率查找它们)基本上是噪音,除非您分析某个紧密循环并发现它存在问题。

1
很酷,谢谢Ben,我想那回答了我的问题。我同意这可能是微不足道的,但是微不足道的事情会累积起来,其他条件相等的情况下,我更喜欢默认使用高效的约定习惯。这样,当你遇到重要的情况时,你已经养成了这个习惯。 - DougW
1
字符串常量,即使用@""定义的常量是单例/原子,它们确实节省了相当多的内存和时间。NSNumbers也是同样的道理。在任何给定的进程中,只有一个由[NSNumber numberWithInt:1]生成的实例。 - TechZen
NSNumber只会对整数的一小部分进行内部化,而常量NSString则始终会被内部化。 - rpetrich
1
好的回答。此外,我想补充一点,使用枚举定义数字常量也是一个不错的方法,无论是在头文件还是实现文件中,因为适当的作用域规则(即名称可以被遮蔽)将会应用。 - Michael Aaron Safyan

8

最近,我开始使用类方法来存储常量。我起初是为了在一个庞大的Core Data模型中存储键名而使用它。然而,从代码和代码库创建和维护的角度来看,这种方式非常高效。我生成了如下的一个类别:

@interface MyClass (KeyNames)
+ (NSString *) creationDate_Key;
@end

@implementation MyClass (KeyNames)

+ (NSString *) creationDate_Key{
    return @"creationDate";
} 
@end

然后我像这样使用它:
NSString *key=[MyClass creationDate_Key];

我有一个脚本可以为我生成方法。好处是它们是有作用域和继承性的,比长长的定义更加紧凑。如果我需要在循环中频繁使用键,只需将其放入本地变量中,如果效率成为问题。


2
只是好奇,当你这样做时,每次引用“creationDate_Key”时,它是否会在内存中创建一个新的NSString对象(@"creationDate")? - Mims H. Wright
不,它不是这样的。使用 @ 创建的字符串是特殊的。 - Christian Kienle

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接