如何在Objective-C(Cocoa)中正确创建充满NSNumbers的静态NSArrays?

时间:2010-10-21 07:16:06

标签: objective-c nsarray nsnumber

为什么在下面的代码中,我不能简单地创建一个NSNumbers的静态数组?我只会使用C数组和整数,但那些无法复制,正如你在init()中看到的那样,我必须将数组复制到另一个数组。我收到的错误是“初始化元素不是常数”。这很混乱;考虑到我在那里的任何地方都没有const关键字,我甚至不确定这意味着什么。

另外,作为旁注,getNextIngredient方法给出了错误“不能将对象用作方法的参数”和“返回不兼容的类型”,但我不确定原因。

以下是代码:

// 1 = TOMATO
// 2 = LETTUCE
// 3 = CHEESE
// 4 = HAM

#import "Recipe.h"




@implementation Recipe

// List of hardcoded recipes
static NSArray *basicHam = [[NSArray alloc] initWithObjects:[[NSNumber alloc] numberwithInt:1], [[NSNumber alloc] numberwithInt:2], [[NSNumber alloc] numberWithInt:3], [[NSNumber alloc] numberwithInt:4]];

// Upon creation, check the name parameter that was passed in and set the current recipe to that particular array.
// Then, set nextIngredient to be the first ingredient of that recipe, so that Game can check it.
-(id) initWithName: (NSString*)name {
    self = [super init];

    indexOfNext = 0;

    if (self) {
        if ([name isEqualToString: @"Basic Ham"]) {
            currRecipe = [NSArray arrayWithArray: basicHam]; 
        }                                
    }
}

-(NSNumber) getNextIngredient {
    return [currRecipe  objectAtIndex:indexOfNext];
}

4 个答案:

答案 0 :(得分:11)

在现代,您将使用dispatch_once()进行一次初始化。 Xcode内置了一个方便的模板。


NSArray永远不是静态分配的对象,因此不能是静态变量的初始化器。

做类似的事情:

@implementation Recipe

+ (NSArray *) basicHam {
    static NSArray *hams;
    if (!hams) 
        hams = [[NSArray alloc] initWithObjects:[NSNumber numberwithInt:1], [NSNumber numberwithInt:2], [NSNumber numberWithInt:3], [NSNumber numberwithInt:4], nil];
    return hams;
}

但请注意以下几点:

  • 我稍微改了你的代码。您没有分配,然后numberWithInt: NSNumber。那不行。

  • 我在参数列表的末尾添加了nil。这是必要的。

而且,必须注意的是,一个有效地包含一小组自然计数数字而没有间隙的数组是非常奇怪的。任何时候x = foo[x]是一个身份表达式,它通常表明使用中的模式有一些奇怪的奇怪之处。

答案 1 :(得分:1)

这样做的经典方法是使用+ initialize方法:

static NSArray *basicHam;

@implementation Recipe

+ (void)initialize {
    if (self == [Recipe class]) {
        basicHam = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2],
                                                    [NSNumber numberWithInt:3], [NSNumber numberWithInt:4, nil]];
    }
}

如果您在C中需要它而不是附加到Obj-C类,则可以使用的替代方法如下所示:

static NSArray *basicHam;

static void initBasicHam() __attribute__((constructor)) {
    basicHam = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2],
                                                [NSNumber numberWithInt:3], [NSNumber numberWithInt:4, nil]];
}

那就是说,我仍然会建议选择bbum的回答,因为这更加惯用。

答案 2 :(得分:0)

这是一个更全面的例子(也使用了概述的可可成语bbum)。它指出了其他一些错误,并提出了你的旁注:

/* Recipe.h */

@interface Recipe : NSObject
{
    NSUInteger indexOfNext;
    NSArray * currentRecipe;
}

- (id)initWithName:(NSString *)name;
- (id)initWithBasicHam;

- (NSNumber *)getNextIngredient;

@end

extern NSString * const Recipe_DefaultRecipeName_BasicHam;

/* Recipe.m */

NSString * const Recipe_DefaultRecipeName_BasicHam = @"Basic Ham";

@implementation Recipe

/* @return list of hardcoded recipes */
+ (NSArray *)basicHam
{
    // there may be a better place to declare these
    enum { TOMATO = 1, LETTUCE = 2, CHEESE = 3, HAM = 4 };
    static NSArray * result = 0;
    if (0 == result) {
        result = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:TOMATO], [NSNumber numberWithInt:LETTUCE], [NSNumber numberWithInt:CHEESE], [NSNumber numberWithInt:HAM], nil];
    }
    return result;
}

/* Upon creation, check the name parameter that was passed in and set the current recipe to that particular array. */
/* Then, set nextIngredient to be the first ingredient of that recipe, so that Game can check it. */
- (id)initWithName:(NSString *)name
{
    self = [super init];
    if (0 != self) {
    /* note: set your ivar here (after checking 0 != self) */
        indexOfNext = 0;

        if ([name isEqualToString:Recipe_DefaultRecipeName_BasicHam]) {
            currentRecipe = [[Recipe basicHam] retain];
        }
    }
    return self;
}

- (id)initWithBasicHam
{
    self = [super init];
    if (0 != self) {
        indexOfNext = 0;
        currentRecipe = [[Recipe basicHam] retain];
    }
    return self;
}

- (NSNumber *)getNextIngredient
{
    assert(currentRecipe);
    return [currentRecipe objectAtIndex:indexOfNext];
}

@end

而不是字符串文字,最好创建字符串键,以及使用便利构造函数,例如- (id)initWithBasicHam(如图所示)。

另外,您通常会调用[[Recipe basicHam] copy]而不是Recipe basicHam] retain] - 但在此特定示例中,这不是必需的。

答案 3 :(得分:0)

这可以使用dispatch_once以线程安全的方式完成。例如:

- (NSArray *)staticSpeeds {
    static dispatch_once_t onceToken;
    static NSArray *speeds;
    dispatch_once(&onceToken, ^{
        speeds = [NSArray
                  arrayWithObjects:[NSNumber numberWithFloat:1.0], nil];
    });
    return speeds;
}