NSNumbers taking up less memory than ints? - objective-c

I'm still very much a noob, having a lot of fun learning the basics of Objective-C, using XCode to put together some simple programs for OS-X.
I have a program which ranks a five card poker hand.
Each card in the deck is identified by its unique 'index number' (0-51)
To speed up the evaluator I thought it would be useful to have an array containing all possible combinations of five indices (there are 2598960 of these).
If I do this:
NSMutableArray *allPossibleHands = [[NSMutableArray alloc] initWithObjects: nil];
for(int i = 0; i<48; i++)
{
for(int j = i+1; j<49; j++)
{
for(int k = j+1; k<50; k++)
{
for(int m = k+1; m<51; m++)
{
for(int n = m+1; n<52; n++)
{
NSNumber *number0 = [NSNumber numberWithInt: i];
NSNumber *number1 = [NSNumber numberWithInt: j];
NSNumber *number2 = [NSNumber numberWithInt: k];
NSNumber *number3 = [NSNumber numberWithInt: m];
NSNumber *number4 = [NSNumber numberWithInt: n];
NSArray *nextCombination = [[NSArray alloc] initWithObjects: number0,number1,number2,number3,number4,nil];
[allPossibleHands addObject: nextCombination];
}
}
}
}
}
NSLog(#"finished building allPossibleHands. It contains %i objects", [allPossibleHands count]
);
everything seems to work fine, and I get a message to say that my array contains, as expected, 2598960 objects. I can then list all the elements of my array.
But I thought wrapping my ints in NSNumber objects like that must take up a lot more memory. Maybe storing the index numbers as short ints would be better.
However, if, instead of building my array as above, I do this:
`short int allPossibleHands[2598960][5]`;
intending to then use my loop to store the ints directly, I get a EXC_BAD_ACCESS error message and a note that there's no memory available to the program.
So how come I can store all those NSNumber objects, but not the ints?
Is there some rule about array construction that I'm breaking?
As always, any guidance much appreciated.
Thank You for reading this.

While the second is allocated on the stack (which is much more limited in size), the first one is allocated on the heap and is a pointer to a memory area.
This does not mean that the first one takes less space. If you allocated the second array as a pointer, the error would go away.
Also read the answers to this question.

Assuming that an NSNumber object must store the value and the type of the number, it is probably a little larger than an int.
But if your int[][] array is a local variable, it is very likely stored on the stack, and most stacks are not that large. You could use a pointer to such an array and malloc it on the heap, which probably has enough room for it.
Accessing a C array is very likely a little faster than accessing that many NSNumbers in an NSArray and extracting their values, and if this is for a card game, speed is probably an issue.

I think the problem is where you are storing your array. Is it on the stack? If so, keep in mind it's going to be 25MB so much larger than most stacks allow.

Related

best way to populate NSArray in this algorithm

I intend to make a program that does the following:
Create an NSArray populated with numbers from 1 to 100,000.
Loop over some code that deletes certain elements of the NSArray when certain conditions are met.
Store the resultant NSArray.
However the above steps will also be looped over many times and so I need a fast way of making this NSArray that has 100,000 number elements.
So what is the fastest way of doing it?
Is there an alternative to iteratively populating an Array using a for loop? Such as an NSArray method that could do this quickly for me?
Or perhaps I could make the NSArray with the 100,000 numbers by any means the first time. And then create every new NSArray (for step 1) by using method arraywithArray? (is it quicker way of doing it?)
Or perhaps you have something completely different in mind that will achieve what I want.
edit: replace NSArray with NSMutableArray in above post
It is difficult to tell in advance which method will be the fastest. I like the block based functions, e.g.
NSMutableArray *array = ...; // your mutable array
NSIndexSet *toBeRemoved = [array indexesOfObjectsPassingTest:^BOOL(NSNumber *num, NSUInteger idx, BOOL *stop) {
// Block is called for each number "num" in the array.
// return YES if the element should be removed and NO otherwise;
}];
[array removeObjectsAtIndexes:toBeRemoved];
You should probably start with a correctly working algorithm and then use Instruments for profiling.
You may want to look at NSMutableIndexSet. It is designed to efficiently store ranges of numbers.
You can initialize it like this:
NSMutableIndexSet *set = [[NSMutableIndexSet alloc]
initWithIndexesInRange:NSMakeRange(1, 100000)];
Then you can remove, for example, 123 from it like this:
[set removeIndex:123];
Or you can remove 400 through 409 like this:
[set removeIndexesInRange:NSMakeRange(400, 10)];
You can iterate through all of the remaining indexes in the set like this:
[set enumerateIndexesUsingBlock:^(NSUInteger i, BOOL *stop) {
NSLog(#"set still includes %lu", (unsigned long)i);
}];
or, more efficiently, like this:
[set enumerateRangesUsingBlock:^(NSRange range, BOOL *stop) {
NSLog(#"set still includes %lu indexes starting at %lu",
(unsigned long)range.length, (unsigned long)range.location);
}];
I'm quite certain it will be fastest to create the array using a c array, then creating an NSArray from that (benchmark coming soon). Depending on how you want to delete the numbers, it may be fastest to do that in the initial loop:
const int max_num = 100000;
...
id *nums = malloc(max_num * sizeof(*nums));
int c = 0;
for(int i = 1; i <= max_num; i++) {
if(!should_skip(i)) nums[c++] = #(i);
}
NSArray *nsa = [NSArray arrayWithObjects:nums count:c];
First benchmark was somewhat surprising. For 100M objects:
NSArray alloc init: 8.6s
NSArray alloc initWithCapacity: 8.6s
id *nums: 6.4s
So an array is faster, but not by as much as I expected.
You can use fast enumeration to search through the array.
for(NSNumber item in myArrayOfNumbers)
{
If(some condition)
{
NSLog(#"Found an Item: %#",item);
}
}
You might want to reconsider what you are doing here. Ask yourself why you want such an array. If your goal is to manipulate an arbitrarily large collection of integers, you'll likely prefer to use NSIndexSet (and its mutable counterpart).
If you really want to manipulate a NSArray in the most efficient way, you will want to implement a dedicated subclass that is especially optimized for this kind of job.

Non-repeating arc4random_uniform

I've been trying to get non-repeating arc4random_uniform to work for ages now for my iPhone app. Been over all the questions and answers relating to this on stackoverflow with no luck and now I'm hoping someone can help me. What I want to do is is choose 13 different random numbers between 1 and 104. I have gotten it to work to the point of it choosing 13 different numbers, but sometimes two of them are the same.
int rand = arc4random_uniform(104);
This is what I'm doing, and then I'm using the rand to choose from an array. If it's easier to shuffle the array and then pick 13 from the top, then I'll try that, but I would need help on how to, since that seems harder.
Thankful for any advice.
There's no guarantee whatsoever that ar4random_uniform() won't repeat. Think about it for a second -- you're asking it to produce a number between 0 and 103. If you do that one hundred and five times, it has no choice but to repeat one of its earlier selections. How could the function know how many times you're going to request a number?
You will either have to check the list of numbers that you've already gotten and request a new one if it's a repeat, or shuffle the array. There should be any number of questions on SO for that. Here's one of the oldest: What's the Best Way to Shuffle an NSMutableArray?.
There's also quite a few questions about non-repeating random numbers: https://stackoverflow.com/search?q=%5Bobjc%5D+non-repeating+random+numbers
You can create an NSMutableSet and implement it like this:
NSMutableArray* numbers = [[NSMutableArray alloc] initWithCapacity: 13];
NSMutableSet* usedValues = [[NSMutableSet alloc] initWithCapacity: 13];
for (int i = 0; i < 13; i++) {
int randomNum = arc4random_uniform(104);
while ([usedValues containsObject: [NSNumber numberWithInt: randomNum]) {
randomNum = arc4random_uniform(104)
}
[[usedValues addObject: [NSNumber numberWithInt: randomNum];
[numbers addObject: [[NSNumber numberWithInt: randomNum];
}
Alternatively you can also create a mutable array of 105 integers each a unique one, and arc4random_uniform([arrayname count]) and then delete that same one from the array, then you'll get a random int each time without repeating (though the smaller the array gets the easier it is to predict what the next number will be, just simple probability)
The best algorithm that I have found for this exact question is described here:
Algorithm to select a single, random combination of values?
Instead of shuffling an array of 104 elements, you just need to loop through 13 times. Here is my implementation of the algorithm in Objective C:
// Implementation of the Floyd algorithm from Programming Pearls.
// Returns a NSSet of num_values from 0 to max_value - 1.
static NSSet* getUniqueRandomNumbers(int num_values, int max_value) {
assert(max_value >= num_values);
NSMutableSet* set = [NSMutableSet setWithCapacity:num_values];
for (int i = max_value - num_values; i < max_value; ++i) {
NSNumber* rand = [NSNumber numberWithInt:arc4random_uniform(i)];
if ([set containsObject:rand]) {
[set addObject:[NSNumber numberWithInt:i]];
} else {
[set addObject:rand];
}
}
return set;
}

Using many integers in Objective-C

I'm new to programming, I have some basic python programming from college, I am familiar with some of the OOP basics and would like some help with managing large amounts of integers. I have 88 of them. 7 will be used for capturing user input and the other 81 will be used for a specific calculation. Instead of writing the following code:
int currentPlace;
int futurePlace;
int speed;
int distance;
int place1 = 1;
int place2 = 2;
int place3 = 3;
// etc...
int place81 = 81;
And then later coming back to the integers and asking user defined questions such as:
NSLog(#"What place is the runner in?");
scanf("%i", &currentPlace);
NSLog(#"What place does the runner finish in?");
scanf("%i", &futurePlace);
NSLog(#"What is the distance of the track?");
// doing some math
NSLog(#"The runner is running at "i" MPH.",speed);
I remember there being an easier way to use the integers but I keep thinking enums or typedefs.
I'd like for the user to pick a number and not have to run a huge if statement to get the work done to cut the size of the program as much as possible.
This is my first "on my own" application so any helpful pointers would be great.
Thanks.
I haven't understood why you need all these place's, but I also assume that an array would be easier to use here. You can use either NSArray or NSMutableArray. The difference between them is that an NSArray instance can't be changed after being created (you can't add/remove elements) unlike an NSMutableArray.
Using NSArray
NSArray *places = [NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2],[NSNumber numberWithInt:3], ..., [NSNumber numberWithInt:81], nil];
nil at the end means the end of the contents of an array. [NSNumber numberWithInt:1] returns an int given as an argument (we can't straight give an int to the array, as an array expects an object as an argument.
You can access the contents of the array using:
[places objectAtIndex:(NSUInteger)];
Remeber that an array starts counting with 0, so if you want to get 5, you have to do this
[places objectAtIndex:4];
Using NSMutableArray
I suggest that you should use this option.
It's easier to use for here.
NSMutableArray *places = [NSMutableArray array];
for (int i = 1; i < 81; i++)
{
[places addObject:[NSNumber numberWithInt:i]];
}
Then you can access data the same way as in the NSArray:
[places objectAtIndex:0];
This will return 1. You can start the for-cycle with 0. After that the index of an array will correspond to the integer inside, so
[places objectAtIndex:5];
will actually return 5.
Are you thinking of a C array?
int myPlaces[81];
for (int i=0; i<81; i++) {
myPlaces[i] = 0;
}

Getting C-style array from objects of NSArray

I have an NSArray arr. It has a bunch of NSNumber objects. I'm trying to calculate statistics analysis on the array using GNU's GSL. GSL takes parameters as C-style arrays.
Is there any mechanism that can, for example, run 'intValue' on all of the objects in a NSArray object, and convert the results that to a C-style array?
I don't really want to copy the contents of the NSArray to a C-style array, as it's a waste of space and cycles, so I'm looking for an alternative.
The mechanism you're describing — run intValue on all the objects in the NSArray and give a C-style array — seems to be exactly the same thing you describe as "a waste of space and cycles." It's also the only real way to do this if you need a C-style array of ints. Best approach I can think of:
int *c_array = malloc(sizeof(int) * [yourArray count]);
[yourArray enumerateObjectsWithOptions:NSEnumerationConcurrent
usingBlock:^(id number, NSUInteger index, BOOL *unused) {
c_array[index] = [number intValue];
}];
Try this:
id *numArray = calloc(sizeof(id), yourArray.count);
[yourArray getObjects: numArray range: NSMakeRange(0, yourArray.count)];
That gives you a C-array of NSNumbers. An alternative that gives you ints:
int *numArray = calloc(sizeof(int), yourArray.count);
for (int i = 0; i< yourArray.count; i++)
numArray[i] = [[yourArray objectAtIndex: i] intValue];
There is no way to tell yourArray to return a C-array of ints directly. NSArray has no concept of the contents it has, except that they are ids, and must be retained and released at the right times. It can at most return a C-array of ids, as in my first example.
You could probably write your own simple array class that contains ints (or floats or doubles, etc.) directly, in an internal C array, but there is no stock class for this.

retrieving doubles in NSMutableArray converted with [NSNumber numberWithDouble:doubleVar]

I am looking for what is probably a simple answer to my novice question. I know that NSMutableArray can only hold objects, so when I put the double into the array I used the [myArray addObject:[NSNumber numberWithDouble:aValue]]; conversion method. What I need to do now is to pull the values out as doubles and average them. I believe I will use the fast enumeration method vs. FoR looping.
I just need to know how to get the following to work.
double total;
total = [myArray objectAtIndex:i]
total = [[myArray objectAtIndex:i] doubleValue];