Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling a subclass's getter in Swift

So, while playing around with Swift, I ran into this issue and it's boggling my mind.

I have a class Card:

class Card {
    var contents = ""
    var chosen = false
    var matched = false

    var description: String {
    get {
        return self.contents
    }
    }

    func match(otherCards: Card[]) -> Int {...}
}

and a class PlayingCard that subclasses Card:

class PlayingCard: Card {
    var suit: Suit
    var rank: Rank

    override var contents: String {
    get {
        return "\(rank.description())\(suit.toRaw())"
    }
    set { }
    }

    init(rank: Rank, suit: Suit) {
        self.suit = suit
        self.rank = rank

        super.init()
    }

    override func match(otherCards: Card[]) -> Int {...}
}

As you can see, in Card, the description property is computed from the contents property. However, when I instantiate a PlayingCard, I have a computed contents property that overrides the stored contents property of Card. However, if I call my PlayingCard's description, it returns the Card's stored contents, not PlayingCard's computed, actual contents.

func testPlayingCardProperties() {
    var card = PlayingCard(rank: .Ace, suit: .Spades)

    XCTAssert(card.contents == "A♠️", "Contents not overriden.") // TRUE
    XCTAssert(card.description == "A♠️", "Description not overriden.") // FALSE??
}

What's going on? Note that

func testCardProperties() {
    var card = Card()
    card.contents = "Test Card"

    XCTAssert(card.contents == "Test Card", "Contents getter error.") // TRUE
    XCTAssert(card.description == "Test Card", "Description getter error.") // TRUE
}

works as expected.

UPDATE

I ran the following in Playground... and it worked. Still no luck outside of it.

class Card {
    var contents = ""
    var chosen = false
    var matched = false

    var description: String {
    get {
        return contents
    }
    }

    func match(otherCards: Card[]) -> Int {
        var score = 0

        for card in otherCards {
            if card.contents == contents {
                score += 1
            }
        }

        return score
    }
}

enum Suit: String {
    case Diamonds = "♦️"
    case Hearts = "♥️"
    case Spades = "♠️"
    case Clubs = "♣️"

    static func allSuits() -> Suit[] {
        return [.Diamonds, .Hearts, .Spades, .Clubs]
    }
}

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King

    func description() -> String {
        switch self {
        case .Ace:
            return "A"
        case .Jack:
            return "J"
        case .Queen:
            return "Q"
        case .King:
            return "K"
        default:
            return String(toRaw())
        }
    }
}

class PlayingCard: Card {
    var suit: Suit
    var rank: Rank

    override var contents: String {
    get {
        return "\(rank.description())\(suit.toRaw())"
    }
    set { }
    }

    let maxRank = 13

    init(rank: Rank, suit: Suit) {
        self.suit = suit
        self.rank = rank
    }

    override func match(otherCards: Card[]) -> Int {
        let allCards = otherCards + [self]
        let testMatches = {
            () -> (Card, Card[])[] in

            var result: (Card, Card[])[] = []

            for i in 0..allCards.count {
                result += (allCards[i], Array(allCards[0..i] + allCards[(i + 1)..allCards.count]))
            }

            return result

            }()

        let scores = testMatches.map({
            (card, otherCards) -> Int in
            if let playingCard = card as? PlayingCard {
                var rankValue = 1.0
                var suitValue = 1.0

                for matchCard in otherCards {
                    if let matchPlayingCard = matchCard as? PlayingCard {
                        if (matchPlayingCard.rank == playingCard.rank) {
                            rankValue *= 8;
                        } else {
                            rankValue /= 2;
                        }

                        if (matchPlayingCard.suit == playingCard.suit) {
                            suitValue *= 2;
                        } else {
                            suitValue /= 2;
                        }
                    }
                }

                if rankValue >= 1.0 {
                    return Int(rankValue)
                } else if suitValue >= 1.0 {
                    return Int(suitValue)
                } else {
                    return 0
                }
            } else {
                return 0
            }
            })

        return scores.reduce(Int.min, combine: { $0 > $1 ? $0 : $1 });
    }
}


var card = PlayingCard(rank: .Jack, suit: .Spades)
card.description

Could this just be an Xcode bug?

like image 892
Atharv Vaish Avatar asked Jun 10 '14 07:06

Atharv Vaish


1 Answers

There seems to be something odd, and likely buggy, going on. If I take this code:

class Base {
    var contents = "Base contents"
    var description: String {
    get {
        return self.contents
    }
    }
}

class Derived : Base {
    override var contents: String {
    get {
        return "Derived contents, dynamically generated"
    }
    set { }
    }
}

let d = Derived()
println(d.contents) 
println(d.description)

...and put it in a single file (in a playground or on in a command-line OS X app), then the output is as follows (and I think, as expected):

println(d.contents) // Prints "Derived contents, dynamically generated"
println(d.description) // Prints "Derived contents, dynamically generated"

However, if I move out the Base class into "base.swift" and the Derived class into "derived.swift", leaving the main code in its own file, making no other changes, then it prints out:

println(d.contents) // Prints "Derived contents, dynamically generated"
println(d.description) // Prints "Base contents"

...which feels like a bug to me.

like image 156
Matt Gibson Avatar answered Nov 15 '22 10:11

Matt Gibson