【问题标题】:static NSString usage vs. inline NSString constants静态 NSString 使用与内联 NSString 常量
【发布时间】:2010-12-28 14:30:01
【问题描述】:

在 Objective-C 中,我的理解是指令 @"foo" 定义了一个常量 NSString。如果我在多个地方使用@"foo",则会引用同一个不可变的 NSString 对象。

为什么我经常看到这个代码sn-p(比如在UITableViewCell重用中):

static NSString *CellId = @"CellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellId];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:CellId];

不仅仅是:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellId"];
if (cell == nil) {
    cell = [[UITableViewCell alloc] initWithStyle:style reuseIdentifier:@"CellId"];

我认为这是为了保护我避免在标识符名称中出现编译器无法捕获的拼写错误。但如果是这样,我就不能:

#define kCellId @"CellId"

并避免静态 NSString * 位?还是我错过了什么?

【问题讨论】:

  • 这也保证指针相等可以给你一些性能优势。据我所知,宏符号没有保证。

标签: ios objective-c nsstring


【解决方案1】:

将文字转换为常量是一种很好的做法,因为:

  1. 如您所说,它有助于避免拼写错误
  2. 如果要更改常量,只需更改一处即可

我更喜欢使用 static const NSString* static NSString* const,因为它比 #define 稍微安全一些。除非我真的需要它,否则我倾向于避免使用预处理器。

【讨论】:

  • 在 gdb 中可以很容易地检查字符串常量。 #define 是一个真正的痛苦。但是你在这里使用 const 是不正确的。它需要是“静态 NSString* const”。您的 const 排序实际上并没有在 objc 中实现任何有用的功能。尝试以这种方式与我的方式重新分配您的字符串变量,您会发现在一种情况下会出现编译器错误,而在另一种情况下则不会。
  • 您实际上并没有避开预处理器。所有@sigils 实际上都是预处理器指令。
  • @uchuugaka 也许他所说的“避免使用预处理器”是指“避免依赖预处理器宏替换(即#define)以及所有可能带来的潜在问题”。
【解决方案2】:

我喜欢这里的所有答案,没有一个简单的例子来说明如何正确声明一个......所以......

如果您希望常量在外部可见(即“全局”)....在标题中声明它...

extern NSString *const MyTypoProneString;

并在.m 文件中定义它,OUTSIDE 任何@implementation 之类的...

NSString * const MyTypoProneString = @"iDoNtKnOwHoW2tYpE";

也就是说...如果您只是想要一个 IS LOCALstatic const 到您的类的实现(甚至是某个方法!)...只需将实现(或方法)的字符串INSIDE声明为...

static NSString *MavisBeacon = @"She's a freakin' idiot";

编辑虽然我确实展示了如何做到这一点...我还没有确信这种风格在任何方面都更好比荒谬的更短、更简单、更少重复的 SINGLE 声明,á la..

#define SomeStupidString @"DefiningConstantsTwiceIsForIdiots"

使用 #define 的...它们不那么烦人了。只是不要让那些讨厌预处理器的玩家失望。

【讨论】:

  • 有点旧,但实际上有一个非常非常好的理由不使用#define - #define 宏基本上是替换 - 所以无论何时你在代码中调用 SomeStupidString,预处理器都会用 替换它新的 字符串文字。使用 extern / static const 将所有引用指向内存中的一个位置。这更多内存效率和性能更高。
  • 很高兴知道...我希望我能弄清楚如何使用可变参数宏 或其他东西en masse 创建它们...我还没有找到一种将它们制作出来的好方法,更不用说不必在两个(呜呜呜呜)文件中拆分声明+实现了...
  • Matt S. 的评论实际上是不正确的。在应用程序中的任何位置,相同的字符串文字都指向同一个对象。创建两个 NSString 文字并检查它们的指针。看来,即使在 RELEASE 中,它们也是同一个对象。
  • Logan - 这一定是最近的编译器优化。如果他们这样做那就太好了:)
  • 考虑到单元重用标识符几乎总是被使用并且只输入两次——一次在情节提要或笔尖中,一次在方法中——而且这两次通常都是从类名中复制粘贴- 将字符串直接放入出队调用是最简单的 IMO。至于拼写错误,无论如何你都会在第一次运行时立即发现,尽管我个人从来没有设法犯过这种拼写错误。
【解决方案3】:

您应该将静态变量设为const

静态变量和宏之间的一个区别是宏不能很好地与调试器配合使用。宏也不是类型安全的。

CC++ 的大部分静态变量与宏建议都适用于 Obj-C。

【讨论】:

  • 感谢您的参考,我没有看到那些。
  • 搜索 SO(通过 Google)"static const" macro 了解更多信息。
【解决方案4】:

不能保证在多个地方使用 @"foo" 时,运行时会为它们使用相同的存储空间,而且跨编译单元或库边界肯定不是这种情况。
我宁愿使用static NSString *string = @"foo",尤其是对于很多文字字符串。

【讨论】:

    【解决方案5】:

    我认为这是为了防止我在标识符名称中出现编译器无法捕获的拼写错误。

    正确。这只是基本的防御性编程实践。编译结果(希望)是相同的。

    但如果是这样,我就不能:

    #define kCellId @"CellId"
    

    并避免静态 NSString * 位?还是我错过了什么?

    是的。但是kCellId 符号将是全局定义的,至少在您的编译单元中是这样。声明一个静态变量会使符号成为该块的本地符号。

    您通常会看到字符串常量被定义为全局变量或静态变量,而不是预处理器定义。这有助于确保您只处理不同编译单元之间的单个字符串实例。

    【讨论】:

      【解决方案6】:

      所以,我来晚了一点,但是在 SO 的 C / C++ 领域中,这已经被多次以各种方式提出,但基本上这里是我对 alex gray 评论的扩展版本:

      任何时候你认为你应该为字符串宏使用#define,你很可能不应该这样做。原因是因为#define 宏基本上是预处理器的正则表达式替换。每当预处理器看到一个被调用的宏时,它就会用你定义的任何东西替换它。这意味着每次都会将一个 new 字符串文字分配到内存中,这在单元重用标识符之类的地方非常糟糕(这就是为什么 Apple 的 UITableViewController 默认代码使用静态的原因)。

      正如 Eonil 所提到的,使用 extern / static const 将所有引用指向内存中的一个位置。这在内存效率和性能上要高得多,这在移动设备上非常重要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多