0

我正在尝试掌握 Objective C 和 Cocoa,所以我可能在这里使用了错误的术语。

我已经为我的主 AppDelegate.h 和 AppDelegate.m 读取的一副纸牌制作了一个 Objective C 类,它有两个方法,deckOfCards 和 pickACard。deckOfCards 只是一个 NSMutableArray,每种卡片类型都以字符串形式写出,然后在 pickACard 中创建一个新数组,如下所示:

-(void)pickACard
{
    DeckOfCards *newDeck = [[DeckOfCards alloc] init];
    int r = arc4random() % 52;
    NSLog (@"The card you picked is: %@, and there are %i cards left", [newDeck     objectAtIndex:r], [newDeck count]);
    [newDeck removeObjectAtIndex:r];
}

但是 XCode 说我不能在这个新数组上使用 objectAtIndex 和 removeObjectAtIndex,所以我不能随机选择一张卡片,然后通过删除数组的那部分来“从包中删除它”。

这在deckOfCards 中确实有效,但是当它被AppDelegate.m 调用时,它会创建一个新数组,所以我会得到不同的卡片,但永远不会从包中删除超过一张。

我猜我没有正确创建这个新数组。

为了更清楚起见,DeckOfCards.h 是这样的:

#import <Foundation/Foundation.h>

@interface DeckOfCards : NSObject
{
@private
}
-(void) deckOfCards;
-(void) pickACard;

@end

DeckOfCards.m 是这样的:

@implementation DeckOfCards
-(void)deckOfCards
{
NSMutableArray *deckOfCards = [NSMutableArray arrayWithObjects:
    @"One of Hearts", @"Two of Hearts"..., nil];
}
-(void)pickACard
{
    DeckOfCards *newDeck = [[DeckOfCards alloc] init];
    int r = arc4random() % 52;
    NSLog (@"The card you picked is: %@, and there are %i cards left",[newDeck     objectAtIndex:r], [newDeck count]);
    [newDeck removeObjectAtIndex:r];
}

@end
4

4 回答 4

4

我在这里看到了一些问题。编译器问题的直接原因是您试图NSMutableArrayDeckOfCards. NSMutableArray相反,您希望在 method 的返回值上调用这些方法-[DeckOfCards deckOfCards]。它看起来像这样:

DeckOfCards *newDeck = [[DeckOfCards alloc] init];
NSMutableArray *deckArray = [newDeck deckOfCards];
int r = arc4random() % 52;
NSLog (@"The card you picked is: %@, and there are %i cards left",[deckArray objectAtIndex:r], [deckArray count]);
[deckArray removeObjectAtIndex:r];

但这将我们引向下一个问题——-deckOfCards实际上并不返回任何东西——它的返回类型被定义为void. 因此,为了将该NSMutableArray实例返回到程序的其余部分,您将不得不使用return它,并使用适当的返回类型定义方法。如果我们只是为了解决这个问题,我们会-deckOfCards像这样改变你的方法:

-(NSMutableArray *)deckOfCards
{
    NSMutableArray *deckOfCards = [NSMutableArray arrayWithObjects:
        @"One of Hearts", @"Two of Hearts"..., nil];

    return deckOfCards;
}

好的,所以现在应该编译。但是这里有一些严重的错误。恐怕我现在没有更多时间来回答这个问题,但我建议你阅读一些介绍性的 Objective-C,重点关注对象和类之间的区别,以及对象的生命周期和内存管理。本质上,没有什么是持久的。无论您调用-pickACard的任何实例多少次DeckOfCards,您总是会获得一个单独的实例实例化,然后该实例将获得一个全新的NSMutableArray实例化。但是这些对象都不会被保留(假设您使用的是自动引用计数)并且它们都会立即消失。因此,您每次都能从-pickACard.

希望这会有所帮助。花一些时间阅读和学习 Objective-C 和面向对象编程的基础知识,您将顺利地使该程序按您的意愿运行。如果您有任何问题,请告诉我,我会尽力回答。

于 2012-09-01T16:39:53.330 回答
3

但是 XCode 说我不能在这个新数组上使用 objectAtIndex 和 removeObjectAtIndex,...</p>

不,这是说您没有尝试在阵列上使用它。

您尝试使用的是 DeckOfCards:

DeckOfCards *newDeck = [[DeckOfCards alloc] init];
int r = arc4random() % 52;
NSLog (@"The card you picked is: %@, and there are %i cards left", [newDeck     objectAtIndex:r], [newDeck count]);
[newDeck removeObjectAtIndex:r];

newDeckDeckOfCards您在第一行创建的。

ADeckOfCards不是数组:

@interface DeckOfCards : NSObject

ADeckOfCards只是一个对象。不只是任何物体;你已经添加了一些东西(deckOfCardspickACard方法)。但它不是一个数组,因此不响应,objectAtIndex:也不是可变数组,因此也不响应removeObjectAtIndex:

那么你在哪里一个数组?好吧,你在你的deckOfCards方法中创建一个:

-(void)deckOfCards
{
NSMutableArray *deckOfCards = [NSMutableArray arrayWithObjects:
    @"One of Hearts", @"Two of Hearts"..., nil];
}

但是那个数组会发生什么?

没有。您创建它,然后将其放在地板上。你不会把它归还给任何叫你的人;事实上,你不能,因为这个方法返回void.

暂时搁置架构问题,假设您对编程非常陌生,这可以原谅,您需要做几件事:

  1. 更改deckOfCards方法的声明以声明它将返回一个NSMutableArray *,而不是void
  2. 在创建返回数组的数组后立即添加一行:

    return deckOfCards;
    

    (请注意,deckOfCards这里指的是数组*。)

  3. 改为pickACard调用deckOfCards。这将创建数组并(一旦您进行了更改#2)将其返回给您。在 中声明一个变量pickACard,类似于您在中声明的变量deckOfCards,并将结果分配deckOfCards给它。

  4. 现在您在 中拥有一个数组pickACard,您可以尝试检索其中一项、记录并删除它。通过将objectAtIndex:andremoveObjectAtIndex:消息发送到数组来做到这一点,而不是发送到不是数组的对象。
  5. 当您使用它时,请更正r. NSArray 索引的类型是NSUInteger,而不是int

现在程序应该可以编译、运行并且几乎可以工作了。

几乎?好吧,你还有一件事要做。

deckOfCards每次创建一个新的牌组;您从该套牌中取出一张牌,但随后忘记了该套牌的所有内容,并且下一次将使用另一个新套牌。

这是您的下一个挑战:更改程序以在初始化时创建一个套牌,并记住并在每次后续抽签时重复使用该套牌。一定要正确处理套牌用尽(没有更多牌)的情况,可能是通过在那一刻创建一个新的套牌。

(您的第一直觉是复制和粘贴。仅作为初稿进行。然后对其进行更改,以使甲板创建代码仅在一个地方,一种用于此目的的方法,并且您从两个地方调用该方法你需要创建一个新的牌组。)

我给你一个提示:实例变量。


*在您的deckOfCards方法中,您将看到两个名为 的东西deckOfCards:该方法和您在其中声明的数组。同名的两个不同的东西。编译器不在乎;方法和变量存在于不同的命名空间中,因此在任何给定时刻知道哪个是哪个都没有问题。但是您可能应该更改其中一个以帮助您自己的理解,更不用说其他人对更改什么的建议(deckOfCards我说“用 做这个”——但deckOfCards我的意思是什么?)。

我建议重命名数组变量。只需命名它就array可以开始了。(但只要它是在该方法中声明的局部变量。)

当您将其设为实例变量时,将名称改回更具体的名称。_deckOfCards是当今常见的命名约定——下划线告诉你(同样,编译器并不关心)这个名字是一个实例变量的名字。

于 2012-09-02T07:21:48.850 回答
2

正如其他评论者所说,您的应用程序结构存在多个问题。我创建了一个示例应用程序来向您展示如何着手解决这个问题。首先,在您的 DeckOfCards 类中,您需要一个属性,该属性指向保存您的牌组的可变数组——我称这只是牌组。然后,您需要一个 init 方法,它不仅返回 DeckOFCards 类的实例,而且还创建和填充该卡片数组。所以这就是我放在 DeckOfCards 类中的内容。

在 .h 中,就是这样:

@interface DeckOfCards : NSObject

@property (retain,nonatomic) NSMutableArray *deck;

@end

而且,在 .m 中:

-(id)init {
    if (self = [super init]) {
        NSArray *cardNames = [NSArray arrayWithObjects:@"Two",@"Three",@"Four",@"Five",@"Six",@"Seven",@"Eight",@"Nine",@"Ten",@"Jack",@"Queen",@"King",@"Ace",nil];
        NSArray *suits = [NSArray arrayWithObjects:@" of Hearts",@" of Diamonds",@" of Spades",@" of Clubs",nil];
        self.deck = [NSMutableArray array];
        for (NSString *aCard in cardNames) {
            for (NSString *aSuit in suits) {
                [self.deck addObject:[aCard stringByAppendingString:aSuit]];
            }
        }
    }
    return self;
}

你想创建这个类的一个实例,并从另一个类中挑选卡片,而不是从 DeckOfCards 类本身中挑选卡片。在本例中,我在应用程序委托中完成了此操作(为简单起见),但最好在某些控制器类中执行此操作。所以,我有一个属性 aNewDeck 来保留对该实例的引用(编译器不会让您使用以“new”开头的名称,不知道为什么,或者这是否是山狮中的新事物)。我在 applicationDidFinishLoading 方法中创建了该新实例,然后将 IBAction 连接到选择卡片的按钮。请注意,我将随机数生成器更改为不使用 52,而是使用数组的计数——如果你不这样做,你将选择比数组更大的数字,因为它变得更小。你应该注意到,为了访问数组,我使用 aNewDeck.deck,这是访问实例 aNewDeck 的属性、deck 的方法。这是.h文件:

@class DeckOfCards;
#import <Cocoa/Cocoa.h>

@interface AppDelegate : NSObject <NSApplicationDelegate>

@property (assign) IBOutlet NSWindow *window;
@property (strong) DeckOfCards *aNewDeck;

-(IBAction)pickACard:(id)sender ;

@end

这是.m:

#import "AppDelegate.h"
#import "DeckOfCards.h"

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    self.aNewDeck = [[DeckOfCards alloc] init];
}

-(IBAction)pickACard:(id)sender {
    if (self.aNewDeck.deck.count != 0) {
        int r = arc4random() % [self.aNewDeck.deck count];
        NSLog (@"The card you picked is: %@, and there are %li cards left", [self.aNewDeck.deck  objectAtIndex:r], [self.aNewDeck.deck count] - 1 );
        [self.aNewDeck.deck removeObjectAtIndex:r];
    }else{
        NSLog(@"Game Over!");
    }
}

我希望这可以帮助您入门。

于 2012-09-01T17:25:56.357 回答
1

DeckOfCards 不是 NSMutableArray 的子类,它也没有实现- objectAtIndex:and- removeObjectAtIndex:方法 - 所以你不能使用这些方法很好。

于 2012-09-01T16:39:38.467 回答