Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

grandM

macrumors 68000
Original poster
Oct 14, 2013
1,551
309
Hi

I've got this question about basics oop. Apparantly I have a problem grasping something about its keys. In attachment I'm adding two files. One file is the standard solution (oplossing Matchismo code in ps). The standard solution works, my alternative (mijn oplossing voor Machismo) does too. But I need some more explanation.

In the file called 'oplossing Matchismo' one finds the model (everything except for CardgameViewController) and the Controller (CardgameViewController). The controller provides the link between model and view. The View I didn't add (can do so if wanted). Here comes the questions:

In the CardgameViewController appears following line:
Code:
Card *card = [self.deck drawRandomCard];
This causes my greatest problem. That card will be uses later on to summit the contents variable. The contents will have some value like 5Hearts. The class Card did have a contents variable. The method drawRandomCard of the class Deck returns indeed a pointer to an instance of the class Card. On initializing deck a pointer to a PlayCardDeck object is being given. In that PlayCardDeck object cards are being made and added of the class PlayingCard. Those cards do have a suit (hearts) and a rank (5).

HERE COMES THE PROBLEM. I'd like to write this:
Code:
PLAYINGCARD *card = [self.deck drawRandomCard];
I do not entirely grasp when the value of the contents of card is set upon 5 Hearts. I've tried to summon the instance variable suit of card (in the CardgameViewController). With reason an error is being thrown. Throwing an
Code:
nslog %@ at card (within CardgameViewController) returns <Playingcard and some numbers>
This puzzles me for two reasons.

Firstly I wonder why the %@ returns a PlayingCard object while on the other hand I can't access the instance variable suits (withing CardgameViewController). I guess this is because the code puts the PlayingCardObject into a generic object of the class Card. An object of the class Card doesn't have this instance variable suit. But why does Xcode gives then 5Hearts as value of contents variable? On calling contents in CardgameViewController I'm calling the method of the class Card. This can't access suit and rank.

Unless the value of contents is set on creating the PlayingCard in PlayingCardDeck. Then again, that would imply that the value of each property is being set as I didn't type self.contents = ... in PlayingCardDeck. Even more, if Xcode would set a value it seems more reasonable this contents would be set on nil. The value of the property would be given on initialisation of PlayCard in PlayCardDeck I suppose so nil.

Hence I felt the need to type
Code:
PlayingCard *card = [self.deck drawRandomCard]
. But I couldn't do so as deck's method drawRandomCard (PlayingCardDeck too as the method was not overriden) returns a pointer to an object of the class Card. If I had been able to use PlayingCard *card I would have understood how the value of card.contents was set upon 5Hearts.

I'm getting the impression that Xcode knows that the Card *card refers to a card of the class PlayingCard (despite the assignment to the generic super class Card). In that case card.contents would override the contents variable of the class Card by the contents variable of the subclass PlayingCard. Not clear then why I can't access the suit in that case.

I've added a second file (mijn oplossing voor Matchismo code in ps). It works too. I had made deck of the class PlayingCardDeck. The standard solution has an object of the class Deck and adds a pointer to an object of the subclass. Which seems to be the same problem. Anyway even in my solution I had to do Card *card instead of PlayingCard *card. (warning was thrown concerning pointers).

Thanks for your reading and answer !!!!

ps: standard solution code
Code:
//
//  Card.h
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Card : NSObject

@property (strong, nonatomic) NSString *contents;

@property (nonatomic, getter = isChosen) BOOL chosen;
@property (nonatomic, getter = isMatched) BOOL matched;

- (int)match: (NSArray *)otherCards;
@end

//
//  Card.m
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "Card.h"
@interface Card()

@end

@implementation Card

-(int)match:(NSArray *)otherCards
{
    int score = 0;
    
    for (Card *card in otherCards)
    {
        if ([card.contents isEqualToString:self.contents])
        {
            score = 1;
        }
    }
    
    return score;
}

@end

//
//  PlayingCard.h
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "Card.h"

@interface PlayingCard : Card

@property (strong, nonatomic) NSString *suit;
@property (nonatomic) NSUInteger rank;

+ (NSArray *)validSuits;
+ (NSUInteger)maxRank;

@end

//
//  PlayingCard.m
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "PlayingCard.h"

@implementation PlayingCard

- (NSString *)contents
{
    NSArray *rankStrings = [PlayingCard rankStrings];
    return [rankStrings[self.rank] stringByAppendingString:self.suit];
}

@synthesize suit = _suit; //because we provide setter and getter

+(NSArray *)validSuits
{
    return @[@"♠",@"♣",@"♥",@"♦"];
}

- (void)setSuit:(NSString *)suit
{
    if ([[PlayingCard validSuits] containsObject: suit]) {
        _suit = suit;
    }

}

- (NSString *)suit
{
    return _suit ? _suit : @"?";
}

+ (NSArray *)rankStrings
{
    return @[@"?", @"A", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9", @"10", @"J", @"Q", @"K"];
}

+ (NSUInteger)maxRank
{
    return [[self rankStrings]count]-1;
}

-(void)setRank:(NSUInteger)rank
{
    if (rank <= [PlayingCard maxRank]) {
        _rank = rank;
    }
}

@end

//
//  Deck.h
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject
- (void)addCard: (Card *)card atTop: (BOOL)atTop;
- (void)addCard: (Card *)card;

- (Card *)drawRandomCard;

@end

//
//  Deck.m
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "Deck.h"

@interface Deck()
@property (strong, nonatomic)NSMutableArray *cards;
@end

@implementation Deck
- (NSMutableArray *)cards
{
    if (!_cards) _cards = [[NSMutableArray alloc]init];
    return _cards;
}

- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
    if (atTop)
    {
        [self.cards insertObject:card atIndex:0];
    }
    else
    {
        [self.cards addObject:card];
    }
}

- (void)addCard:(Card *)card
{
    [self addCard:card atTop:NO];
}

-(Card *)drawRandomCard
{
    Card *randomCard = nil;
    
    if ([self.cards count])
    {
        unsigned index = arc4random() % [self.cards count];
        randomCard = self.cards[index];
        [self.cards removeObjectAtIndex:index];
    }
    
    return randomCard;
}

@end

//
//  PlayingCardDeck.h
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "Deck.h"

@interface PlayingCardDeck : Deck

@end

//
//  PlayingCardDeck.m
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "PlayingCardDeck.h"
#import "PlayingCard.h"

@implementation PlayingCardDeck
-(id)init
{
    self = [super init];
    
    if (self)
    {
        for (NSString *suit in [PlayingCard validSuits])
        {
            for (NSUInteger rank = 1; rank <= [PlayingCard maxRank] ; rank++) {
                PlayingCard *card = [[PlayingCard alloc]init];
                card.suit = suit;
                card.rank = rank;
                [self addCard:card];
            }
        }
    }
    
    return self;
}

@end

//
//  CardgameViewController.h
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface CardgameViewController : UIViewController

@end

//
//  CardgameViewController.m
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "CardgameViewController.h"
#import "PlayingCardDeck.h"


@interface CardgameViewController ()
@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;

@property (strong, nonatomic) Deck *deck;

@end

@implementation CardgameViewController

- (Deck *)deck
{
    if (!_deck) _deck = [self createDeck];
    return _deck;
}

- (Deck *)createDeck
{
    return [[PlayingCardDeck alloc]init];
}



- (void) setFlipCount:(int)flipCount
{
    _flipCount=flipCount;
    self.flipsLabel.text=[NSString stringWithFormat:@"Flips: %d", self.flipCount];
}

- (IBAction)touchCardButton:(UIButton *)sender
{
    
    if ([sender.currentTitle length])
    {
        [sender setBackgroundImage:[UIImage imageNamed:@"cardback"]
                          forState:UIControlStateNormal];
        [sender setTitle:@"" forState:UIControlStateNormal];
        self.flipCount++;
    }
    else
    {
        Card *card = [self.deck drawRandomCard];
        NSLog(@"%@", card);
        if(card)
        {
            [sender setBackgroundImage:[UIImage imageNamed:@"cardfront"]
                              forState:UIControlStateNormal];
            [sender setTitle:card.contents forState:UIControlStateNormal];
            self.flipCount++;
        }
    }
    
}

@end

ps: my solution .m controller

Code:
//
//  CardgameViewController.m
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "CardgameViewController.h"
#import "PlayingCardDeck.h"
#import "PlayingCard.h"

@interface CardgameViewController ()
@property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
@property (nonatomic) int flipCount;

@property (strong, nonatomic) PlayingCardDeck *kaardenDek;

@end

@implementation CardgameViewController

- (PlayingCardDeck *)kaardenDek
{
    if (!_kaardenDek) _kaardenDek = [[PlayingCardDeck alloc]init];
    return _kaardenDek;
}



- (void) setFlipCount:(int)flipCount
{
    _flipCount=flipCount;
    self.flipsLabel.text=[NSString stringWithFormat:@"Flips: %d", self.flipCount];
}

- (IBAction)touchCardButton:(UIButton *)sender
{
    Card *kaart = [self.kaardenDek drawRandomCard];
    if ([sender.currentTitle length]) {
        [sender setBackgroundImage:[UIImage imageNamed:@"cardback"]
                          forState:UIControlStateNormal];
        [sender setTitle:@"" forState:UIControlStateNormal];

    } else {
        [sender setBackgroundImage:[UIImage imageNamed:@"cardfront"]
                          forState:UIControlStateNormal];
        [sender setTitle:kaart.contents forState:UIControlStateNormal];
    }
    self.flipCount++;
    [self.kaardenDek addCard:kaart];
}

@end
 

Attachments

  • mijn oplossing voor Matchismo.doc
    24.5 KB · Views: 107
  • Oplossing matchismo.doc
    41 KB · Views: 278
Last edited:
.docs?

A suggestion: include the code you want to share directly in your post. Surround the code with
Code:
 [/code ] tags so that it'll be formatted properly. Most people won't be bothered to open attached files. And .doc is a crappy format for sharing code - use the correct format for the language (IE, obj-C code is generally in a .m or .h file.)
 
code

.docs?

A suggestion: include the code you want to share directly in your post. Surround the code with
Code:
 [/code ] tags so that it'll be formatted properly. Most people won't be bothered to open attached files. And .doc is a crappy format for sharing code - use the correct format for the language (IE, obj-C code is generally in a .m or .h file.)[/QUOTE]

True but the forum didn't allow the extensions. Couldn't upload the Xcode file either. But tx, I've put the code in brackets!
 
Last edited:
True but the forum didn't allow the extensions. Couldn't upload the Xcode file either. But tx, I've put the code in brackets!

Normally people zip their projects (right click the files and choose to make an archive) and upload the zip file.
 
This show a problem that I have as well: Being able to see the layout of the class.

As a class become more complex, it's harder to keep track of the relationship between all the part of the class.

I've seen tools that would analyze the code and show a graphic layout of the class, but haven't see anything like this for ObjC/Xcode.

From what you've posted, you are unable to use/access "suit of cards"? You mean validSuits method that returns the array of valid suits?
 
hi

This show a problem that I have as well: Being able to see the layout of the class.

As a class become more complex, it's harder to keep track of the relationship between all the part of the class.

I've seen tools that would analyze the code and show a graphic layout of the class, but haven't see anything like this for ObjC/Xcode.

From what you've posted, you are unable to use/access "suit of cards"? You mean validSuits method that returns the array of valid suits?

Hi tx for replying. I'm actually referring to the suit property of PlayingCard. This property is set in the init of PlayingCardDeck. In CardgameViewController I can't access the suit property on card. Xcode however seems to know that card's a PlayingCard even though it's returned as on object of the superclass Card. Don't fully understand how Xcode can fill the value of contents with rank and suit in CardgameViewController...
 
Hi tx for replying. I'm actually referring to the suit property of PlayingCard. This property is set in the init of PlayingCardDeck. In CardgameViewController I can't access the suit property on card. Xcode however seems to know that card's a PlayingCard even though it's returned as on object of the superclass Card. Don't fully understand how Xcode can fill the value of contents with rank and suit in CardgameViewController...

Unless I missed something, suit is NOT a property of any class. validSuits just returns an array and that is used to "load up" an array of card objects.

In other words, what you end up with is a character string that has what the card value is (example: 3 of hearts is the character 3 and the heart character).

So if you want to know what suit a card is, you'd have to get the string (contents), get the sub-part of that string where the suit character is.

You could (if you wanted to) store the suit character in a separate property.

The reason it's stored as a string, is that the "face" side of the card is changed to that for the display.

If I've missed something, show me where the suit is a property. I don't think it is a property the way this is designed.
 
suit

Unless I missed something, suit is NOT a property of any class. validSuits just returns an array and that is used to "load up" an array of card objects.

In other words, what you end up with is a character string that has what the card value is (example: 3 of hearts is the character 3 and the heart character).

So if you want to know what suit a card is, you'd have to get the string (contents), get the sub-part of that string where the suit character is.

You could (if you wanted to) store the suit character in a separate property.

The reason it's stored as a string, is that the "face" side of the card is changed to that for the display.

If I've missed something, show me where the suit is a property. I don't think it is a property the way this is designed.

Hi, suit is a property of PlayingCard, validSuits's a class method indeed.
Code:
//  PlayingCard.h
//  Matchismo
//
//  Created by m on 10/12/13.
//  Copyright (c) 2013 m. All rights reserved.
//

#import "Card.h"

@interface PlayingCard : Card

@property (strong, nonatomic) NSString *suit;
 
Wow, i tried so hard to follow this, i really want to help but it is soo hard. zip up the project and post it.

And let us know what you are trying to achieve, very simply.

Thanks.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.