NSarray count throwing out of bounds - objective-c

I am trying to check the following and all throw an out of bounds error
if ([objects count] < 1)
if ([objects count] == 0)
if ([objects count] <= 0)
All three thrown an out of bounds? How do I use this in an "if/else" statement? Is it not possible to check if an NSArray contains zero objects.

If you just defined the pointer but did not initialize your objects instance, you will get very strange behavior. As a beginning cocoa developer, I got burned at least twice by something like
NSMutableArray *objects;
...
if ( [objects count] == 0 )
{
.... // do something
}
Are you sure you initialized it, e.g. like so:
NSMutableArray *objects = [NSMutableArray array];
because it really should work in that case!

I don't think that these lines of code can cause such an error.
This code will work correctly for any array.
Out of bounds exception means that you tried to call something like
[myArray objectAtIndex:0];
On an empty array. Or, in other words, you tried to access an object that wasn't a part of the array.

You should not get an out of bounds exception since you are not even trying to access an element in the array -- an out of bounds exception should only be thrown if you call objectAtIndex on your array and specify an illegal index.
In general, to prevent against attempting to access a nil or empty array, I usually do something like this:
if(!objects || !objects.count) ...
This way, if objects doesn't exist then objects.count will not be called.

You can also try this.
if(![objects lastObject])
{
//... Do Something
}

Related

Objective-C: NSArray of UISwitches

Ok so I;m building an interface to allow admins controlls over certain rules which are controlled using UISwitches (a lot of them)
so I've declared an array of switches
NSMutableArray *cameraSwitches = [[NSMutableArray alloc] initWithObjects:
loc0CamProfileSwitch, loc1CamProfileSwitch, loc2CamProfileSwitch,
loc3CamProfileSwitch, loc4CamProfileSwitch, loc5CamProfileSwitch,
loc6CamProfileSwitch, loc7CamProfileSwitch, loc8CamProfileSwitch, nil];
where each object inside the Array is a UISwitch,
and then I'm looping through a diff array checking for initial values of these switches
for (NSUInteger i = 0; i < [camP count]; i++) {
if ([camP objectAtIndex:i] != 0) {
//UISwitch *csw = [cameraSwitches objectAtIndex:i];
//[csw setOn:YES];
[[cameraSwitches objectAtIndex:i] setOn:YES];
else { [loc7CamProfileSwitch setOn:NO]; }
}
But everything I'm trying is giving me an error at runtime, throwing an exception
'NSRangeException', reason: '*** -[__NSArrayM objectAtIndex:]: index 0 beyond bounds for empty array'
using break points: I realize theres something wrong with this line
[[cameraSwitches objectAtIndex:i] setOn:YES]; - or with how i initiated the array any help is very very appreciated
cameraSwitches is correctly instantiated. that's why you get an exception raised when you try to reach index 0.
But, did you checked that loc0CamProfileSwitch (index 0) is non nil?
Most likely, cameraSwitches is getting deallocated after it's instantiated. This could happen if you create it in a different method than when you're calling objectAtIndex and you're not setting it up as an iVar or #property (strong).
If that doesn't resolve the issue, put a breakpoint on [[cameraSwitches objectAtIndex:i] setOn:YES] and type po cameraSwitches in the debug console.

replaceObjectAtIndex:withObject does not work

I am trying to assign objects in an NSMutableArray using this code
- (IBAction)variablePressed:(UIButton *)sender {
NSString *variable = [sender currentTitle];
if (!_variableToBePassedIntoTheDictionary) _variableToBePassedIntoTheDictionary = [[NSMutableArray alloc] init];
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable];}
but when I run this program the program breaks at the last line, since I have set the debugger to show warnings if a Exception is raised. Running the program without breakpoints, the program gives SIGARBT and crashes. I then assign these values to a dictionary which would be passed to the Model for further calculations.
- (IBAction)testVariableValues:(id)sender {
if (!_variablesAssignedInADictionary) _variablesAssignedInADictionary = [[NSMutableDictionary alloc] init];
[_variablesAssignedInADictionary setObject:_digitToBePassedIntoTheVariable forKey:_variableToBePassedIntoTheDictionary];
NSLog(#"%#", _variablesAssignedInADictionary);}
P.S. I am new in Objective C, can anybody please explain when do we use
#synthesize someProperty;
vs
#synthesize someProperty = _someProperty;
Thank You!
The first time the method is called you create the NSMutableArray and then attempt to replace an object which is not there. The reference says:
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject
The index of the object to be replaced. This value must not exceed
the bounds of the array. Important Raises an NSRangeException if
index is beyond the end of the array.
And 0 will exceed the bounds of an empty array.
Try this instead:
- (IBAction)variablePressed:(UIButton *)sender
{
NSString *variable = [sender currentTitle];
if (_variableToBePassedIntoTheDictionary == nil)
{
_variableToBePassedIntoTheDictionary = [[NSMutableArray alloc] init];
[_variableToBePassedIntoTheDictionary addObject:variable];
}
else
{
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable];
}
}
Taken from the docs :
The index of the object to be replaced. This value must not exceed the
bounds of the array.
As I see from your code your array is initialized and there is no object at index 0. hence you try to replace an object at an index which is out of bounds as your array is empty.
Very simple question:
You told it stops on an exception. Fair enough. What for an exception? Let me guess, an out of bounds exception? The exception tells you what's wrong in most cases.
replaceObjectAtIndex:0 : is there something at that index or not? Probably not.
In your code you test the condition:
if(!_variableToBePassedIntoTheDictionary)
and if the condition is true, that is the array is nil, then you alloc-init it.
In the following statement:
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable];,
you try to replace the object at index 0 with variable. But in the case above, if you just alloc-init the array, it is empty and than you cannot replace the object at index 0 as not existing, and this raises an exception:
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM replaceObjectAtIndex:withObject:]: index 0 beyond bounds for empty array'
So what you have to do is to change the last line as follows:
if([_variableToBePassedIntoTheDictionary count]==0) {
[_variableToBePassedIntoTheDictionary addObject:variable]
} else {
[_variableToBePassedIntoTheDictionary replaceObjectAtIndex:0 withObject:variable]
}
As far as the second question about properties, consider that the role of synthesize is to create for you the setter/getter methods based on the attributes you assigned to the #property. In the new Objective-C you don't need to declare the ivar associated to the property (the ivar is the instance variable that represent the property) and the compiler by default assigns the ivar the name of the property. By using the
#synthesize someProperty = _someProperty
convention you specify that you want the ivar to be called _someProperty. The advantage of this approach with respect to the default one is that you cannot confuse accessing to a property using the setter/getter methods and the ivar directly, that is you cannot make possible mistakes like:
someProperty=value
but instead you must write:
_someProperty=value
or
self.someProperty=value
Anyway have a look at Obj-C documentation for this, it is quite exhaustive.

Is myiVar = nil supported in ARC?

I'm using a code snippet like
if ([Array count] != 0) {
Array = nil;
}
Array = [[NSMutableArray alloc]init];
Is this allowed when using ARC? Does this cause any kind of crashes? Why I'm doing this is each time when my method gets called Array gets a new set of data. I'm using this kind of snippet in many places of my class.
ARC aside, the operation is pointless. You assign an ivar to nil then immediately assign to something else. This is no different from just assigning it to the something else.
Before ARC this would have given you a memory leak (with or without your assignment to nil) if there variable had a previous value. With ARC there is no leak.
Best solution : test and see by yourself ! This is a really short example !
But yes, this works of course !
ARC means Automatic Reference Counting. It just says you don't have to care about release, retain and so on.
If you want an object to be nil, you still can, as it is a simple pointer assignment !
And about your code, you set Array to nil before re-assigning it oO !
Try to get the logic of your code :
If my Array has objects
Then Array point to a new nil object
But in all case you do:
My Array point to a new NSMutableArray object
So whether or not your condition is evaluated to true, your code is useless as the variable will take another value just after !
The sample code you showed is valid. Here are a few variations:
if ([Array count] != 0) {
Array = [[NSMutableArray alloc] init];
}
Example 2:
if ([Array count] != 0) {
Array = [NSMutableArray array]; //value will be retained
}
Example 3:
if ([Array count] != 0) {
self.Array = [[NSMutableArray alloc] init]; //Will NOT leak under ARC.
}
Example 4:
//if ([Array count] != 0) {
[Array removeAllObjects];
//}
All four examples are valid under ARC.
Another thing: instance variables typically have their first letter lowercase, while still following the CamelCase method. Another way to do it is to have an underscore before the name of the variable: this is done if you don't have a #synthesize method to match your #property value.

Adding objects from another array

I have the following code:
for (int i = 1; i <= [nmaSpread count];)
{
[nmaUserName addObjectsFromArray:[nmaSpread objectAtIndex:i]];
[nmaSpread removeObjectAtIndex:i];
i += 2;
}
I have declared all variables as global, nmaUserName and nmaSpread are both NSMutableArrays, and have been allocated in viewDidLoad.
I want to store all the odd objects from nmaSpread into nmaUsername and then delete the active object at nmaSpread.
However it keeps crashing with this error:
[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray
2011-12-11 21:08:55.123 appName[15671:f803] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray'
nmaSpread itself is an NSMutableArray, but it looks like the objects it contains aren't. When you do [nmaSpread objectAtIndex:i], that returns an object from nmaSpread. This object isn't an array, so to add it, you'd just use addObject:, not addObjectsFromArray:.
There are a few problems with your code.
First off, you've miss-understood what -addObjectsFromArray: does. The actual method you want is just addObject:.
Second, it is dangerous to modify an array while looping through it. The official line from Apple is "this may work in some situations, but don't do it because it might stop working at any point in the future". You need to wait until after you have finished looping through the array, and then delete them. Your current code is definitely doing it wrong.
You could sit down with pen/paper and work out the math to keep it all intact, but it's easier and safer to just do this:
NSMutableIndexSet *indexesToRemove = [NSMutableIndexSet indexSet];
for (int i = 1; i <= [nmaSpread count];)
{
[nmaUserName addObject:[nmaSpread objectAtIndex:i]];
[indexesToRemove addIndex:i];
i += 2;
}
[mmaSpread removeObjectsAtIndexes:indexesToRemove];
NSMutableArray's addObjectsFromArray expects you to pass another array, but you are passing a single object (the one at index 'i')
You can try switching
[nmaUserName addObjectsFromArray:[nmaSpread objectAtIndex:i]];
to
[nmaUserName addObject:[nmaSpread objectAtIndex:i]];
and that will remove the error you are seeing, but then you are likely to run into another problem because you are removing objects from nmaSpread as you go and as a result the indexes for items later in the array get shifted. You should probably change your logic around to deal with that problem.

NCSFDictionary, Mutating method sent to immutable object

I have just started to jump into the realm of Objective-C and am slowly getting it all. I have been working on unarchiving a file that was a NSMutableArray and then initializing in my model with that array. The array is filled with various NSMutableDicationary's. From what I have seen it will add those dictionaries as non-mutable, so I went ahead and copied the regular and put them in a mutable and remove the old one. This solution seems to work for every instance except the very first.
I am at a loss as to why it would work for all but the first.
Here is how I am initializing it all
-(id) initWithList:(NSMutableArray *)savedList
{
self = [super init];
if (self)
{
int size=0;
serverList=[[NSMutableArray alloc] initWithArray:savedList copyItems:YES];
size=[serverList count];
for(int i=0;i<size;i++)
{
loginList=[NSMutableDictionary dictionaryWithDictionary:[serverList objectAtIndex:i]];
[serverList addObject:loginList];
[serverList removeObjectAtIndex:i];
}
}
return self;
}
Here is the code that is throwing the error, The value is being read off of a checkbox in a tableview and passed here to change the value.
-(void)setMount:(int)row value:(NSNumber*)boolAsNumber
{
[[serverList objectAtIndex:row] setObject:boolAsNumber forKey:#"mountshare"];
}
Here is the error that it shows when I try and change the first element
2010-12-01 13:38:54.445 Network Share[35992:a0f] *** -[NSCFDictionary setObject:forKey:]: mutating method sent to immutable object
Thanks for your help. If there is a better way please let me know.
This loop code is wrong:
size=[serverList count];
for(int i=0;i<size;i++)
{
loginList=[NSMutableDictionary dictionaryWithDictionary:[serverList objectAtIndex:i]];
[serverList addObject:loginList];
[serverList removeObjectAtIndex:i];
}
When you remove an object, the array is renumbered. After you've processed the 1st object at index 0, the original 2nd object is becoming the 1st object at index 0, but i is now set to index 1, which is where the original 3rd object is! This means you're only processing alternate items from the original array, and the 2nd, 4th, etc items never get swapped, and that's why you get the errors you're seeing.
One way to solve this would be to replace the "i" in the objectAtIndex: and removeObjectAtIndex: calls with "0", so you're always taking items off the front of the array.
The alternate solution would be to create a separate newServerList array and insert your new objects into that. At the end of the loop, release the old serverList and set the variable to point to newServerList.
Your indexes are messed up. As soon as you remove the object at index 0, the next one will take it's place and you will never replace that, because you then carry on with index 1.
{immutable0, immutable1}
i = 0:
addObject:
{immutable0, immutable1, mutable0}
removeObjectAtIndex:
{immutable1, mutable0}
i = 1:
addObject:
{immutable0, mutable0, mutable02}
removeObjectAtIndex:
{immutable0, mutable02}
--> still got the immutable there. Remember to never remove objects from a mutable array you are looping through at the same time.
You could condense the code a bit:
NSMutableArray *serverList = [NSMutableArray arrayWithCapacity:[savedList count]];
for (NSDictionary *dictionary in savedList)
{
mutable = [dictionary mutableCopy];
[serverList addObject:mutable];
[mutable release];
}
Unrelated to your problem: the argument is obviously wrong (NSMutableArray), if you expect an immutable array there; and if you create your serverList that way, there is no need for a deep copy (copyItems:YES).