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

发布于 2024-09-28 17:19:14 字数 1181 浏览 9 评论 0原文

为什么在下面的代码中,我不能简单地创建一个 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];
}

Why is it that in the following code, I can not just simply make a static array of NSNumbers? I would just use C arrays and ints, but those cannot be copied and as you can see in init(), I have to copy the array to another one. The error I recieve is "Initializer element is not constant." It's very confusing; I'm not even sure what that means considering I don't have the const keyword anywhere in there.

Also, as a sidenote, the getNextIngredient method gives me the error "cannot use object as a parameter to a method" and "incompatible types in return", but I'm not sure why.

Here is the code:

// 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];
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

未央 2024-10-05 17:19:14

在现代,您可以使用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] 是一个恒等表达式,它通常表明所使用的模式确实有一些奇怪的地方。

In modern times, you would use dispatch_once() to do the one time initialization. Xcode has a handy template built in for doing exactly that.


An NSArray is never a statically allocated object and, thus, cannot be the initializer for a static variable.

Do something like:

@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;
}

However, note a couple of things:

  • I changed your code slightly. You don't alloc, then numberWithInt: an NSNumber. That won't work.

  • I added a nil at the end of the argument list. That is necessary.

And, still, it must be observed that an array that effectively contains a small set of natural counting numbers in order with no gaps is quite distinctly odd. Anytime that x = foo[x] is an identity expression, it typically indicates there is something decidedly odd about the patterns in use.

你的心境我的脸 2024-10-05 17:19:14

执行此操作的经典方法是使用 +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 的答案,因为这更惯用。

The classic way of doing this is with an +initialize method:

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]];
    }
}

An alternative which works if you need this in C instead of attached to an Obj-C class is something like the following:

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]];
}

That said, I would still recommend going with bbum's answer, as that's far more idiomatic.

浊酒尽余欢 2024-10-05 17:19:14

这是一个更彻底的示例(也使用了可可习语 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]——但这在这个特定示例中不是必需的。

here's a more thorough example (which also uses the cocoa idiom bbum outlined). it points out a few other errors, and addresses your sidenote:

/* 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

instead of string literals, it may be best to create string keys, as well as use convenience constructors, such as - (id)initWithBasicHam (as demonstrated).

also, you'd typically call [[Recipe basicHam] copy] rather than Recipe basicHam] retain] -- but that is not necessary in this particular example.

无名指的心愿 2024-10-05 17:19:14

这可以通过 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;
}

This can be done in a thread safe manner with dispatch_once. For example:

- (NSArray *)staticSpeeds {
    static dispatch_once_t onceToken;
    static NSArray *speeds;
    dispatch_once(&onceToken, ^{
        speeds = [NSArray
                  arrayWithObjects:[NSNumber numberWithFloat:1.0], nil];
    });
    return speeds;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文