2010-10-21 49 views
1

爲什麼在下面的代碼中,我不能簡單地創建一個NSNumbers的靜態數組?我只是使用C數組和整數,但這些數據不能被複制,並且你可以在init()中看到,我必須將數組複製到另一個數組中。我收到的錯誤是「初始化元素不是常量」。這很混亂;我甚至不確定這是否意味着考慮到我沒有const關鍵字。如何在Objective-C(Cocoa)中正確地創建完整的NSNumbers?

另外,作爲旁註,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]; 
} 

回答

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]都是一個標識表達式,它通常表明對於正在使用的模式有一些明顯的奇怪之處。

+0

有關這方面的問題 - 在火腿初始化過程中不會出現競爭條件嗎? dispatch_once模式不會更好嗎? Yup; – 2012-06-18 18:24:34

+0

dispatch_once()是一個更好的解決方案。我認爲*這是在公開API之前編寫的。 :) – bbum 2012-06-18 21:33:29

1

這樣做的經典方法是使用+初始化方法:

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的答案,因爲這更加地道。

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] - 但在此特定示例中這不是必需的。