0

私は Objective C と Cocoa を理解しようとしているので、ここで間違った用語を使用している可能性があります。

メインの AppDelegate.h と AppDelegate.m によって読み取られるカードのデッキ用の Objective C クラスを作成しました。これには、deckOfCards と pickACard の 2 つのメソッドがあります。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

ここにはいくつかの問題があります。NSMutableArrayコンパイラの問題の直接の原因は、 のインスタンスでメソッドを呼び出そうとしていることですDeckOfCardsNSMutableArray代わりに、メソッドによって返されたでこれらのメソッドを呼び出す必要があります-[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;
}

OK、これでコンパイルできるはずです。しかし、ここにはいくつかの重大な誤りがあります。申し訳ありませんが、現時点ではこの回答に費やす時間があまりありませんが、オブジェクトとクラスの違い、およびオブジェクトの有効期間とメモリ管理に焦点を当てた、Objective-C の入門書を読むことをお勧めします。基本的に、永続化されるものはありません。-pickACardの任意のインスタンスを何度呼び出してもDeckOfCards、インスタンス化された別のインスタンスが常に取得され、その後、まったく新しいNSMutableArrayインスタンス化が行われます。ただし、これらのオブジェクトはどれも保持されず (自動参照カウントを使用していると仮定)、すべてがすぐに消えます。そのため、常に新しい 52 枚のカード デッキを から入手できます-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は単なるオブジェクトです。オブジェクトだけではありません。それに何かを追加しました(deckOfCardsおよびpickACardメソッド)。しかし、それは配列ではないため、 に応答せobjectAtIndex:ず、可変配列ではないためremoveObjectAtIndex:、 にも応答しません。

では、配列はどこにありますか? さて、あなたはあなたのdeckOfCardsメソッドでそれを作成します:

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

しかし、その配列はどうなるでしょうか。

何もない。あなたはそれを作成し、それを床に落とします。あなたはあなたを呼んだ人にそれを返しているのではありません。実際、このメソッドは を返すため、できませんvoid

プログラミングに非常に慣れていないことを前提として許されるアーキテクチャの問題を一時的に脇に置いて、いくつかのことを行う必要があります。

  1. methodの宣言を変更して、ではなくdeckOfCardsを返すことを宣言します。NSMutableArray *void
  2. 配列を作成した直後に、配列を返す行を追加します。

    return deckOfCards;
    

    deckOfCards(ここでは配列*を参照していることに注意してください。)

  3. pickACard通話に変更deckOfCards。これにより配列が作成され、(変更 #2 を行ったら) それが返されます。pickACardで宣言したものと同様の変数を で宣言しdeckOfCards、結果を代入しdeckOfCardsます。

  4. に配列が作成されたpickACardので、その項目の 1 つを取得してログに記録し、削除することができます。これを行うには、objectAtIndex:およびremoveObjectAtIndex:メッセージを、配列ではないオブジェクトではなく、配列に送信します。
  5. その間に、 の型を修正しますr。NSArray インデックスはNSUIntegerではなく型intです。

これで、プログラムがコンパイルされ、実行され、ほとんど動作するはずです。

ほとんど?さて、もう 1 つやるべきことがあります。

deckOfCards毎回新鮮なデッキを作成します。そのデッキから 1 枚のカードを削除しますが、そのデッキのことはすべて忘れて、次回は別の新しいデッキを使用します。

これが次の課題です。初期化でデッキを作成するようにプログラムを変更し、その後のドローごとにそのデッキを記憶して再利用します。おそらくその時点で新しいデッキを作成することにより、デッキが枯渇した場合(カードがなくなる)を正しく処理するようにしてください.

(あなたの最初の本能は、コピーして貼り付けることです。それは最初のドラフトとしてのみ行ってください。次に、デッキ作成コードが 1 つの場所、その目的のためのメソッドだけにあるように変更し、そのメソッドを両方の場所から呼び出します。新しいデッキを作成する必要があります。)

ヒントをあげましょう: インスタンス変数。


*メソッドには、そのメソッドと、その中で宣言する配列deckOfCardsという名前の 2 つのものが表示されます。deckOfCards同じ名前の 2 つの異なるもの。コンパイラは気にしません。メソッドと変数は別々の名前空間に存在するため、いつでもどちらがどちらであるかを知るのに問題はありません。しかし、何を変更すべきかについて他の人が提案したことは言うまでもなく、自分自身の理解を助けるためにどちらか一方を変更する必要があります (「これを実行してくださいdeckOfCards」と私は言いますが、どちらdeckOfCardsを意味するのでしょうか?)。

配列変数の名前を変更することをお勧めします。名前を付けるだけarrayで、最初は十分です。(ただし、そのメソッド内で宣言されたローカル変数である場合に限ります。)

インスタンス変数にするときは、名前をより具体的なものに戻します。_deckOfCardsは、最近の一般的な命名規則です。アンダースコアは、この名前がインスタンス変数の名前であることを示しています (コンパイラは気にしません)。

于 2012-09-02T07:21:48.850 に答える
2

他のコメンターが言ったように、アプリの構造に複数の問題があります。この問題への取り組みを開始する方法を示すサンプル アプリを作成しました。まず、DeckOfCards クラスで、カードのデッキを保持する可変配列を指すプロパティが必要です。私はこれを単にデッキと呼びました。次に、DeckOFCards クラスのインスタンスを返すだけでなく、そのカードの配列を作成および設定する init メソッドが必要です。これが私が 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 のプロパティであるデッキにアクセスする方法である 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:および- removeObjectAtIndex:メソッドも実装していません。したがって、これらのメソッドを使用できなくても問題ありません。

于 2012-09-01T16:39:38.467 に答える