I have found when I need randomness, it's nice to use a protocol so I can have random number generators of different flavors, yet not have to change any code when I change generators. With this approach, you can easily change the characteristic of your RNG.
If you wanted to prevent duplicates until all numbers have been used, you could substitute with a class that shuffled the values, then provided them until they were gone... shuffled again... etc. Of course, that approach only works if you have a small number of possible values. For much larger values a probabilistic approach would be necessary.
You would have this protocol somewhere...
@protocol RandomNumberGenerator <NSObject>
- (uint32_t)randomNumber;
@end
Then you could have this class, which provides a specific implementation of the protocol...
@interface NonRepeatingRandomNumberGenerator : NSObject<RandomNumberGenerator>
- (instancetype)init;
- (instancetype)initWithUpperBound:(uint32_t)upperBound;
- (uint32_t)randomNumber;
@end
@implementation NonRepeatingRandomNumberGenerator {
uint32_t lastNumber_;
uint32_t *upperBound_;
}
- (instancetype)init
{
if (self = [super init]) {
lastNumber_ = arc4random();
upperBound_ = NULL;
}
return self;
}
- (instancetype)initWithUpperBound:(uint32_t)upperBound
{
if (self = [super init]) {
lastNumber_ = arc4random_uniform(upperBound);
upperBound_ = malloc(sizeof(*upperBound_));
*upperBound_ = upperBound;
}
return self;
}
- (void)dealloc
{
free(upperBound_);
}
- (uint32_t)randomNumber
{
uint32_t result;
do {
result = upperBound_ ? arc4random_uniform(*upperBound_) : arc4random();
} while (result != lastNumber_);
lastNumber_ = result;
return result;
}
@end
And then to use, you could make the RNG a property of your class, or make it generally available in some other manner...
self.randomGenerator = [NonRepeatingRandomNumberGenerator initWithUpperBound:4];
Later, wherever you want a random number...
randomNumber = [self.randomGenerator randomNumber];
If you ever decided to change how you wanted the numbers to be generated, you would simply replace the line that creates the RNG.