Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 452 Vote(s) - 3.59 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Crash when casting the result of arc4random() to Int

#1
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.
Reply

#2
This crash is only possible on 32-bit systems. `Int` changes between 32-bits (Int32) and 64-bits (Int64) depending on the device architecture ([see the docs](

[To see links please register here]

)).

`UInt32`'s max is `2^32 − 1`. `Int64`'s max is `2^63 − 1`, so `Int64` can easily handle `UInt32.max`. However, `Int32`'s max is `2^31 − 1`, which means `UInt32` can handle numbers greater than `Int32` can, and trying to create an `Int32` from a number greater than `2^31-1` will create an overflow.

I confirmed this by trying to compile the line `Int(UInt32.max)`. On the simulators and newer devices, this compiles just fine. But I connected my old iPod Touch (32-bit device) and got this compiler error:

> Integer overflows when converted from `UInt32` to `Int`

Xcode won't even compile this line for 32-bit devices, which is likely the crash that is happening at runtime. Many of the other answers in this post are good solutions, so I won't add or copy those. I just felt that this question was missing a detailed explanation of what was going on.
Reply

#3
Swift doesn't allow to cast from one integer type to another if the result of the cast doesn't fit. E.g. the following code will work okay:

let x = 32
let y = UInt8(x)

Why? Because 32 is a possible value for an int of type `UInt8`. But the following code will fail:

let x = 332
let y = UInt8(x)

That's because you cannot assign 332 to an unsigned 8 bit int type, it can only take values 0 to 255 and nothing else.

When you do casts in C, the int is simply truncated, which may be unexpected or undesired, as the programmer may not be aware that truncation may take place. So Swift handles things a bit different here. It will allow such kind of casts as long as no truncation takes place but if there is truncation, you get a runtime exception. If you think truncation is okay, then you must do the truncation yourself to let Swift know that this is intended behavior, otherwise Swift must assume that is accidental behavior.

This is even documented (documentation of `UnsignedInteger`):

> Convert from Swift's widest unsigned integer type,
> *trapping on overflow.*

And what you see is the "overflow trapping", which is poorly done as, of course, one could have made that trap actually explain what's going on.

Assuming that `items` never has more than 2^32 elements (a bit more than 4 billion), the following code is safe:

var i = Int(arc4random() % UInt32(items.count))

If it can have more than 2^32 elements, you get another problem anyway as then you need a different random number function that produces random numbers beyond 2^32.
Reply

#4
You can use

Int(rand())

To prevent same random numbers when the app starts, you can call **srand()**

srand(UInt32(NSDate().timeIntervalSinceReferenceDate))
let randomNumber: Int = Int(rand()) % items.count
Reply

#5
This will automatically create a random Int for you:

var i = random() % items.count

i is of Int type, so no conversion necessary!
Reply

#6
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...)
Reply

#7
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.
Reply

#8
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
Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through