【问题标题】:What is the best way to define string constants in an objective-c protocol?在 Objective-C 协议中定义字符串常量的最佳方法是什么?
【发布时间】:2009-12-08 11:33:51
【问题描述】:

我已经定义了一个我所有插件都必须实现的协议。我还希望插件都使用某些字符串,例如 MyPluginErrorDomain。使用整数,这很容易在枚举中实现,但我不知道如何对字符串做同样的事情。通常,在类中我会定义

extern NSString * const MyPluginErrorDomain;

在 .h 文件和 .m 文件中:

NSString * const MyPluginErrorDomain = @"MyPluginErrorDomain";

但这在协议中效果不佳,因为每个插件都必须提供自己的实现,这违背了拥有常量的目的。

然后我尝试了

#define MYPLUGIN_ERROR_DOMAIN @"MyPluginErrorDomain"

但插件中的实现类似乎看不到#define。谁知道一个好的解决方案?

【问题讨论】:

  • 删除 C,C++ 标签 - 这是客观的 C 问题
  • 以 C++ 着称,另请参阅我对 Neil Butterworth 的有趣回答的评论。我仍然想知道是否也没有 C 解决方案,我不能成为 C 历史上第一个想要实现这一目标的人。
  • 因为这些是 C 常量,所以这个 C 不需要被删除

标签: objective-c cocoa nsstring constants protocols


【解决方案1】:

您可以在带有协议的标头中声明它们(但在协议接口本身之外),然后在协议的实现文件中定义它们(显然它不会有 @implementation 部分 - 只是您的 NSString定义)。

或者有一个单独的.h/.m 对只用于字符串常量(协议头可以导入字符串常量头)。

【讨论】:

    【解决方案2】:

    您保留 .h 定义:

    extern NSString * const MyPluginErrorDomain;
    

    但将此部分放入一个单独的 .m 文件中,该文件将包含在您的框架中:

    NSString * const MyPluginErrorDomain = @"MyPluginErrorDomain";
    

    所以插件仍然可以实现接口,但是在编译时它们会链接或编译到您的其他 .m 文件中,因此它们会看到 MyPluginErrorDomain 的值。

    【讨论】:

      【解决方案3】:

      在 C++ 中,我会在这样的标题中声明它们:

      const char * const MYPLUGIN_ERROR_DOMAIN = "MyPluginErrorDomain";
      const char * const MYPLUGIN_FOO_DOMAIN = "MyPluginFooDomain";
      

      请注意,由于指针是const,它们将在标头#include 的翻译单元中是本地的,因此无需使用extern 来防止多个定义错误。

      【讨论】:

      • 翻译单元的本地对所有 const 指针定义都有效吗?
      • 所有 const 定义都是如此——指针只是一种情况。请注意,指针本身必须是 const,所以 const char * p = "foo";不会。
      • 对于那些像我一样发现这令人惊讶的人:“具有命名空间范围的名称如果是 .. 的名称,则具有内部链接。明确声明为 const 且未明确声明 extern 的对象或引用..” [3.5/3]
      • 我很惊讶人们不知道这一点——它是 C++ 更好的特性之一。也许我找到了下一个博客主题 :-)
      • @Neil:很喜欢 'const char *const',我已经知道“是一个指向不可变对象的不可变指针”[Clark S. Cox, III on objectmix.com/c/177819-nsstring-char.html],但我我们还发现它不能很好地与 NSString 配合使用。我将删除 c++ 标签以防止我的困惑蔓延。写关于这个主题的博客,因为你可以看到这里的市场有一个大洞。
      【解决方案4】:

      您应该像示例中那样将其实现为外部字符串:

      extern NSString * const MyPluginErrorDomain;

      或提供返回静态存储数据的外部函数。例如:

       /* h */ 
      
       extern NSString * MyPluginErrorDomain();
      
       /* m */ 
      
       NSString * MyPluginErrorDomain() {
          static NSString * const s = @"MyPluginErrorDomain";
          return s;
       }
      

      原因是字符串和键经常被使用并通过指针值或哈希值进行比较,而不是真正的字符串比较(isEqualToString:)。

      在实现层面,有很大区别:

      在代码中,这意味着当比较的字符串在多个二进制文件中定义时:

      假设 'MyPluginErrorDomain' 和 'key' 具有相同的字符串值,但定义在不同的二进制文件中(即在插件主机中,一个在插件中)。

      /////// Pointer comparison (NSString)
      BOOL a = [MyPluginErrorDomain isEqualToString:key];
      BOOL b = MyPluginErrorDomain == key;
      
      // c may be false because a may be true, in that they represent the same character sequence, but do not point to the same object
      BOOL c = a == b;
      
      
      /////// Hash use (NSString)
      // This is true
      BOOL d = [MyPluginErrorDomain hash] == [key hash];
      
      // This is indicative if true
      BOOL e = [MyPluginErrorDomain hash] == [someOtherStringKey hash];
      
      // because
      BOOL f = [MyPluginErrorDomain isEqualToString:someOtherStringKey];
      
      // g may be false (though the hash code is 'generally' correct)
      BOOL g = e == f;
      

      因此在很多情况下都需要提供密钥。这似乎是微不足道的一点,但很难诊断出与差异相关的一些问题。

      哈希码和指针比较在 Foundation 和字典存储内部的其他 objc 技术中使用,键值编码......如果你的字典直接输出到 xml,那是一回事,但运行时使用是另一回事在实现和运行时细节中有一些注意事项。

      【讨论】:

      • extern 字符串不起作用,因为您无需链接到二进制文件即可通过 protocol 与其通信 - 想想 远程消息传递
      • @gf 插件链接到特定于插件接口的库的情况并不少见。
      • “Not uncommon” 不是 “always” - 远程消息传递也是 Cocoa 的“common”
      • @Justin:你能澄清一下你的答案吗?我不知道您所说的“将其实现为客户链接的外部字符串”是什么意思。我也不理解“字符串/键经常被指针值/哈希码用作/比较为哈希值”。插件将使用字符串常量作为字典中的键。
      • @Elise 我重写了响应以阐明哈希/指针/字符串比较和使用差异
      猜你喜欢
      • 2013-06-18
      • 1970-01-01
      • 1970-01-01
      • 2011-03-27
      • 1970-01-01
      • 1970-01-01
      • 2018-02-01
      • 2018-11-05
      • 1970-01-01
      相关资源
      最近更新 更多