removeObjectsAtIndexes causing a crash - ios

Can't figure out whats causing the crash here. Im enumerating over a NSMutableArray and placing the indexes of certain objects into a mutable index set, then attempting to remove the corresponding objects from the array. here is my code
NSMutableIndexSet *itemsToRemove;
UIBarButtonItem *item;
NSUInteger index;
for (item in self.rightView.toolbarItems)
{
if (item.tag == MLPAD_RIGHT_VC_BACK_TO_CENTRAL_ITEM_TAG){
[itemsToRemove addIndex:index];
index ++;
}
}
[self.rightView.toolbarItems removeObjectsAtIndexes:itemsToRemove];
the last line is crashing and giving me an EX_BAD_ACCESS.
any ideas why?
thanks

You're not allocating/initializing itemsToRemove - if you're using ARC, it's initialized to nil, if not, it potentially contains garbage - neither one is acceptable if you want it to be passed as an argument later...
Neither do you initialize index to zero...
(Why am I suspecting that you're coming from a managed language?...)
Unrelated to the crash, but it's still a semantic error: you have to increment index even if the conition is not met:
for (item in self.rightView.toolbarItems)
{
if (item.tag == MLPAD_RIGHT_VC_BACK_TO_CENTRAL_ITEM_TAG) {
[itemsToRemove addIndex:index];
}
index ++;
}

Your primary problem is that itemsToRemove never gets initialized. You can't add an object to an NSMutableIndexSet that isn't really an NSMutableIndexSet - try setting itemsToRemove = [[NSMutableIndexSet alloc] init] or similar.
I also think where you have the statement index++ might be throwing some things off - if you only increment the index when you find an item you want to remove, you're effectively removing the first itemsToRemove.count items, instead of the actual items you want to get rid of. Think about incrementing the index unconditionally instead.

You have several issues. Not allocating itemsToRemove, not initilializing index (builtin stack variables are not auto-initialized) and not incrementing index on every loop iteration.
NSMutableIndexSet *itemsToRemove = [NSMutableIndexSet new];
NSUInteger index = 0;
for (UIBarButtonItem *item in self.rightView.toolbarItems)
{
if (item.tag == MLPAD_RIGHT_VC_BACK_TO_CENTRAL_ITEM_TAG){
[itemsToRemove addIndex:index];
}
++index;
}
[self.rightView.toolbarItems removeObjectsAtIndexes:itemsToRemove];
The index type of error can be minimized by using the block enumeration method, which passes in the current index.

Related

NSMutableArray Extra Nil Sentinels

I've read a text file into an array of strings. In the below code I'm creating objects from that array and adding them into an NSMutableArray.
NSMutableArray* metaphors = [[NSMutableArray alloc] init];
unsigned int i, cnt = [allLinedStrings count];
for(i = 0; i < cnt-3; i+=5)
{
Metaphor *newMetaphor = [[Metaphor alloc] init];
[newMetaphor setMetaphorTitle: allLinedStrings[i]];
[newMetaphor setCorrectAnswer: allLinedStrings[i+1]];
[newMetaphor setLiteralAnswer: allLinedStrings[i+2]];
[newMetaphor setWayOffAnswer: allLinedStrings[i+3]];
[metaphors addObject:newMetaphor];
}
As for the problem, when I access any item via index ([metaphors objectAtIndex:3] for example) every other element (odd numbered ones) are nil elements. All of the objects are added to the array, though. My guess is that addObject is adding an element to the array as well as a new nil sentinel every time? Should this be happening/should I manually go through and remove these elements?
Also a side note, as I'm new to Objective-C, my Metaphor class contains the 4 instance fields you can see within the body: I'm sure there is quicker syntax to initialize one of these objects if anyone could point me the right way. Thanks.
NSArray can't contain nil. It's invalid. If you are getting nil back in a call to an array, the array pointer itself is almost certainly nil. (You CAN send messages to a nil object pointer in Objective C. It simply returns nil/zero.)
Trying to add a nil to an NSArray will cause a crash, and trying to index past the end of an NSArray will also crash.
There is a special class NSNull that provides a singleton placeholder object that can take the place of a nil entry in an NSArray.
The nil sentinel is only a way to know the last element of a variable length array has been reached. The nil sentinel ISN'T added to the NSMutableArray;
NSMutableArray addObject method doesn't need a nil sentinel as you only add ONE object, not a variable length array.
If you need to add something "nil" to an array, you might use [NSNull null] which is an object "equivalent to nil".

cannot insert into NSMutableArray due to indexvalue being out of bounds

I have a MutableArray on this view called array and the object in question is detailItem, which has a property of rank (int). On this view, there's a text field displaying the rank and I want to be able to move the detailItem up and down the MutableArray by changing the rank.
So, for example let's say the detailItem has a rank of 3, which is index value of 2. If I change this in the text field to 3, I want the array to adjust and move it down one place. However, as I type in the value of rankField (the text field), it crashes the app since it automatically updates the value before I'm done editing. So, if I click on the text field and write 23 (planing on deleting the 2) or just press delete (now the value is nil) the app crashes with an uncaught exception.
Here's the code:
- (IBAction)rankFIeldTextChanged:(id)sender {
QueueMember *member = self.detailItem;
[self.array removeObjectAtIndex:self.detailItem.rank];
if (0<= [self.rankField.text intValue]<= self.array.count) {
[self.array insertObject:member atIndex:[self.rankField.text intValue]-1];
}
}
The if condition of making the text value in-between the array size and 0 seems to have no effect.
btw this is all in the detailsViewController which is connected to the main view controller via push segue. does it make more sense(or more better coding) to just set the new rank value in details and actually make the array changes in the mainviewcontroller.m?
The problem is that you are trying to do two boolean statements at once (which doesn't work). Change your if statement to something like:
if (0< [self.rankField.text intValue] && [self.rankField.text intValue] < self.array.count) {
//Insert your object here
}
else
{
//Add object here
}
Your current setup check to see if 0<= [self.rankField.text intValue], which will return true for all values greater than or equal to 0. Then it checks the result of that (YES:1, NO:0) if it's less than or equal to your array count. That will always return true if your array has anything in it. So basically your check will always return true.
Since it always returns true I could check for array object number 1000, your if statement says go for it, then I check and the array says "No way in heck!" and crashes your app.
EDIT: Updated my code snippet to take into account your array insertion line.
I'd just do this
- (IBAction)rankFIeldTextChanged:(id)sender {
QueueMember *member = self.detailItem;
[self.array removeObjectAtIndex:self.detailItem.rank];
if ((0<= [self.rankField.text intValue])&&([self.rankField.text intValue]<= self.array.count)) {
if (self.array.count >([self.rankField.text intValue]-1)){
[self.array insertObject:member atIndex:[self.rankField.text intValue]-1];
}
}
}
you are probably trying to insert at an index greater than the count of the array.

Objective-C how to pull several random items out of NSArray without getting duplicates? [duplicate]

This question already has answers here:
Getting a random object from NSArray without duplication
(3 answers)
Closed 7 years ago.
I have an array of random properties I would like to assign to equipment within the game I'm developing.
The code that I use below is returning an NSArray. I'm interested if there's way to get item indices from that array without getting duplicate values. The obvious solution is to create a mutable array with the returned array, do random, remove item that was returned and loop until the number of items is received.
But is there a different way of getting X random items from NSArray without getting duplicates?
//get possible enchantments
NSPredicate *p = [NSPredicate predicateWithFormat:#"type = %i AND grade >= %i", kEnchantmentArmor,armor.grade];
NSArray* possibleEnchantments = [[EquipmentGenerator allEnchantmentDictionary] objectForKey:#"enchantments"];
//get only applicable enchantments
NSArray *validEnchantments = [possibleEnchantments filteredArrayUsingPredicate:p];
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray:validEnchantments];
NSDictionary* enchantment = nil;
if(mutableArray.count>0)
{
//got enchantments, assign number and intensity based on grade
for (int i = 0; i<3;i++)
{
enchantment = mutableArray[arc4random()%mutableArray.count];
[mutableArray removeObject:enchantment];
//create enchantment from dictionary and assign to item.
}
}
You can shuffle the array using one of the following techniques:
What's the Best Way to Shuffle an NSMutableArray?
Non repeating random numbers
Then, take the first X elements from the array.
Many years ago, I was working on card game and I realized that shuffling the deck was an inefficient way to get random cards. What I would do in your shoes is pick a random element, and then replace it with the element at the end of the array, like so:
#interface NSMutableArray (pickAndShrink)
- (id) pullElementFromIndex:(int) index // pass in your random value here
{
id pickedItem = [self elementAtIndex:index];
[self replaceObjectAtIndex:index withObject:[self lastObject]];
[self removeLastObject];
return pickedItem;
}
#end
The array will shrink by one every time you pull an element this way.
You could use a random number generator to pick a starting index, and then pick the subsequent indices based on some kind of math function. You would still need to loop depending on how many properties you want.
Eg:
-(NSMutableArray*)getRandomPropertiesFromArray:(NSArray*)myArray
{
int lengthOfMyArray = myArray.count;
int startingIndex = arc4random()%lengthOfMyArray;
NSMutableArray *finalArray = [[NSMutableArray alloc]init]autorelease];
for(int i=0; i<numberOfPropertiesRequired; i++)
{
int index = [self computeIndex:i usingStartingIndex:startingIndex origninalArray:myArray];
[finalArray addObject:[myArray objectAtIndex:index]];
}
return finalArray;
}
-(int)computeIndex:(int)index usingStartingIndex:(int)startingIndex
{
//You write your custom function here. This is just an example.
//You will have to write some code to make use you don't pick an Index greater than the length of your array.
int computedIndex = startingIndex + index*2;
return startingIndex;
}
EDIT: Even your computeIndex function could use randomness in picking the subsequent indices. Since you have a startingIndex, and another index, you could use that to offset your function so that you never pick a duplicate.
EDIT: If your array is very large, and the subset you need to pick is small, then rather than shuffle the entire array (maybe more expensive), you could use this method to pick the number of items you need. But if your array is small, or if the number of items you need to pick are almost the size of the array, then the godel9's solution is better.
You can use a mutable array and then remove them as the are selected, use something like random()%array.count to get a random index. If you don't want to modify the array then copy it with [array mutableCopy].

Problems with objectAtIndex of NSMutableArray

I am developing a game for iPhone in which I have 2 arrays.
One with the objects "living" in the game, and when these objects "die" they are placed in another array to be removed from the first one in the end of the first loop.
This is necessary because if the object dies in the loop and it is removed immediately from the first array, the index is lost and sometimes gives an invalid address access.
Then I have an NSMutableArray that contains objects to be removed from the living objects array.
In the array with objects to remove, I am constantly adding and removing items. Sometimes when I use objectAtIndex with the array with items to remove, it is retuning me other object.
For example in the first moment _enemiesToRemove has 2 objects:
([0]-> 0x0a8b5120, [1]->0x18f3a090)
for(int i = [_enemiesToRemove count] - 1; i >= 0 ; i--){
fish = [_enemiesToRemove objectAtIndex:i];
[self removeEnemy:fish];
}
and then
int i = [_enemiesToRemove count] - 1
gives i=1 but when it is doing
[_enemiesToRemove objectAtIndex:i]
it is returning me other object with address 0xbfffcd58 and it should be [1]->0x18f3a090
What is happening here, somebody knows? It is not happening always. It happens randomly.

xcode check if there is aobjectAtIndex or check array length

I cant find this any where, I may be searching the wrong terms or words but I just need to know how to check if an array is a certain length:
if ([scores objectAtIndex:3]){
//code
}
This comes up with an error and crashes if the array isnt this long yet, but surly this should just check if there is an index, and if not move on??
How to I check this without the app crashing??
count method of NSArray returns the number of objects in the array. If [myArray count] returns n then valid indexes are 0 to n - 1. There is no automatic move on if the index is not valid. Before trying to access an index you need to make sure that the index is valid.
if ([scores count] >= 4) {
id obj = [scores objectAtIndex:3];
}

Resources