Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Crash when casting the result of arc4random() to Int

Tags:

random

swift

I've written a simple Bag class. A Bag is filled with a fixed ratio of Temperature enums. It allows you to grab one at random and automatically refills itself when empty. It looks like this:

class Bag {
    var items = Temperature[]()

    init () {
        refill()
    }

    func grab()-> Temperature {
        if items.isEmpty {
            refill()
        }

        var i = Int(arc4random()) % items.count
        return items.removeAtIndex(i)
    }

    func refill() {
        items.append(.Normal)

        items.append(.Hot)
        items.append(.Hot)

        items.append(.Cold)
        items.append(.Cold)
    }
}

The Temperature enum looks like this:

enum Temperature: Int {
    case Normal, Hot, Cold
}

My GameScene:SKScene has a constant instance property bag:Bag. (I've tried with a variable as well.) When I need a new temperature I call bag.grab(), once in didMoveToView and when appropriate in touchesEnded.

Randomly this call crashes on the if items.isEmpty line in Bag.grab(). The error is EXC_BAD_INSTRUCTION. Checking the debugger shows items is size=1 and [0] = (AppName.Temperature) <invalid> (0x10).

Edit Looks like I don't understand the debugger info. Even valid arrays show size=1 and unrelated values for [0] =. So no help there.

I can't get it to crash isolated in a Playground. It's probably something obvious but I'm stumped.

like image 296
Shaun Inman Avatar asked Jun 06 '14 17:06

Shaun Inman


3 Answers

Function arc4random returns an UInt32. If you get a value higher than Int.max, the Int(...) cast will crash.

Using

Int(arc4random_uniform(UInt32(items.count))) 

should be a better solution.

(Blame the strange crash messages in the Alpha version...)

like image 119
Sulthan Avatar answered Sep 22 '22 03:09

Sulthan


I found that the best way to solve this is by using rand() instead of arc4random()

the code, in your case, could be:

var i = Int(rand()) % items.count 
like image 44
vyudi Avatar answered Sep 23 '22 03:09

vyudi


This method will generate a random Int value between the given minimum and maximum

func randomInt(min: Int, max:Int) -> Int {
    return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}

The crash that you were experiencing is due to the fact that Swift detected a type inconsistency at runtime. Since Int != UInt32 you will have to first type cast the input argument of arc4random_uniform before you can compute the random number.

like image 39
Groot Avatar answered Sep 24 '22 03:09

Groot