【问题标题】:How do I declare an array as a constant in Objective-c?如何在 Objective-c 中将数组声明为常量?
【发布时间】:2011-01-27 01:06:09
【问题描述】:

以下代码给了我错误:

//  constants.h
extern NSArray const *testArray;
//  constants.m
NSArray const *testArray = [NSArray arrayWithObjects:  @"foo", @"bar", nil];

我得到的错误是
initializer element is not constant

或者,如果我去掉指针指示符 (*),我会得到:
statically allocated instance of Objective-C class 'NSArray'

【问题讨论】:

  • “常数”是什么意思?对象的内容不可变,还是指针不可变?
  • 嗯,我的标准是:1) 值不能被意外更改(所以我猜 NSArray 很好),2) 在程序的任何地方都可用(我在我的 . pch 文件) , 3) 只声明一次

标签: objective-c arrays constants


【解决方案1】:

简而言之,你不能。除了 NSString 之外,Objective-C 对象只在运行时创建。因此,您不能使用表达式来初始化它们。

有几种方法。

(1) 声明 NSArray *testArray 而不使用 const 关键字,然后编写一些代码来设置在应用程序生命周期的早期调用的值。

(2) 声明一个返回数组的便捷类方法,然后在该方法中使用static NSArray *myArray 并将其视为单例(在 SO 中搜索“objective-c 单例”以获取有关如何实例化的无数答案)。

【讨论】:

  • 如果您只处理问题中的NSStrings 数组,您可以在常量文件中创建一个C 样式数组,然后使用一些C 方法从那里访问值。见my answer below
  • @bbum -- 即使使用@[...] 糖,这仍然是正确的,是吗?
  • 有人可能会争辩说,块现在是成熟的可保留对象类型,它们是第二种类型的 Objective-C 对象,编译器知道布局并可以创建常量实例。可以从块文字初始化静态存储持续时间变量。
  • @NikolaiRuhe 是的,但如果只允许静态块初始化让编译器给你“非静态初始化”错误,并且看似无害的变化,那将是一件非常痛苦的事情。
【解决方案2】:

聚会有点晚了,但是由于您没有在整个程序过程中更改值,因此如果您只处理字符串,则可以通过使用 C 数组声明您的数组来执行以下操作:

extern NSString * const MY_CONSTANT_STRING_ARRAY[];

在您的 constants.h 文件中,然后在您的 constants.m 中,您可以像这样向其中添加对象:

NSString * const MY_CONSTANT_STRING_ARRAY[] = { @"foo", @"bar" };

然后要访问成员,您可以使用C sizeof() 运算符执行 for 循环:

这显然是一个C 数组而不是NSArray,因此您不会像objectAtIndex: 那样获得所有附加到它的有趣方法,因此您可以在程序中的某处创建一个循环的辅助函数所有字符串都使用我上面概述的方法并返回NSArray(甚至NSMutableArray)。但是,如果您正在做我正在做的事情,并且只需要在整个程序中使用一个常量数组 NSString 值,那么这种方法效果最好。

这样做会将您的所有字符串数组内容封装在constants.h 中,并且仍然可以通过在您的.pch 文件中添加constants.h 来在整个程序中使用,而不是仅为这个值数组或设置创建一个单例带有一点代码的数组,这有点违背了constants 文件的目的,因为它从constants 文件中删除了实际的常量..

根据@JesseGumpo 的评论进行编辑

由于使用sizeof() 确定数组的大小可能存在问题,一个简单的解决方法是在常量文件中声明数组的大小,如下所示:

//.h
extern int SIZE_OF_MY_CONSTANTS_ARRAY;  

///.m
int SIZE_OF_MY_CONSTANTS_ARRAY = 2;

然后要访问 for 循环中的成员,您可以这样做:

for (int i=0; i < SIZE_OF_MY_CONSTANTS_ARRAY; i++) 
        NSLog(@"my constant string is: %@", MY_CONSTANT_STRING_ARRAY[i]);

是的,这不会动态捕获数组的大小,但是如果您在常量文件中声明一个数组,您从一开始就已经知道该数组的大小,所以即使它添加了两行代码,它仍然完成了在常量文件中包含数组的任务。

如果有人有更多建议或可能知道其他C 技巧,请在下方留言!

【讨论】:

  • 这是不正确的。 sizeof() 返回内存大小。这可能会经历 8 次迭代,但在第三次失败
  • @JesseGumpo,感谢您的评论。在我的实际实现中,我为数组大小创建了一个 int 常量,而不是使用sizeof()。将编辑我的答案以反映我的最终方法,因为它使用简单的数据类型,仍然可以工作并完成在常量文件中包含数组的任务。
  • "sizeof(MY_CONSTANT_STRING_ARRAY) / sizeof(MY_CONSTANT_STRING_ARRAY[0])" 会做到的
【解决方案3】:

这是一个宏,可以在一行中为方法范围内的静态实例执行此操作。

#define STATIC_ARRAY(x, ...)   \
        static NSArray* x=nil; \
        static dispatch_once_t x##onceToken; \
        dispatch_once(&x##onceToken, ^{ x = @[ __VA_ARGS__ ]; });

使用示例

    STATIC_ARRAY(foo, @"thing1", @"thing2", [NSObject new]);

【讨论】:

  • 这是一个整洁的sn-p!非常喜欢!
【解决方案4】:

这很简单:

#define arrayTitle [NSArray arrayWithObjects: @"hi",@"foo",nil]

放在实现之前,不带分号。

希望对你有帮助。

【讨论】:

  • 这并没有真正给你一个常量数组,它只是在你每次使用arrayTitle时生成一个新数组,
  • 但是,它确实可以帮助稍后访问该线程的其他人,他们只想在头文件中声明,但不关心制作常量。
  • 它绝对不会创建一个常量,但它确实创建了一个总是给出相同数组的数组,这几乎就是常量数组的作用,对吧?顺便说一句,很棒的答案!
  • 不是常量数组,反正买,我的问题最好的解决方案
  • 预处理宏也是邪恶的。
【解决方案5】:

对于我来说,对于常量数组使用下面的实现会更方便

static NSString * kHeaderTitles [3] = {@ "ACCOUNT DETAILS", @ "SOCIAL NETWORK", @ "SETTINGS"};
static int kNumbers[3] = {1, 2, 3};

【讨论】:

    【解决方案6】:

    我有一个名为“Constants.h”的头文件,在下一个常量数组中:

    #define arrayOfStrings @[@"1", @"2", @"3", @"4"]
    #define arraysOfIds @[@(1), @(2), @(3), @(4)]
    

    基本上,当您在代码中调用arrayOfStrings 时,会被@[@"1", @"2", @"3", @"4"] 替换,而arraysOfIds 也一样。

    【讨论】:

    • 这也将导致每次提到arrayOfStringsarraysOfIds 时都会创建一个数组的新实例
    猜你喜欢
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-21
    • 2010-09-25
    • 2011-09-05
    • 1970-01-01
    相关资源
    最近更新 更多