Adding objects from another array - objective-c

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.

Related

Why is calling subarrayWithRange multiple times in a loop causing a NSMallocException?

I have a large NSArray (wordDictionary) and I am creating smaller sub-arrays from it inside a for-loop. If the for-loop is set to 20,000 iterations everything works just fine. But if I increase the for-loop iterations to 200,000 iterations I get a malloc error... Why is that?
I noticed that if I move the sub-array assignment from inside the loop to outside the loop, it solves the problem!(?) Note that all sub-arrays are identical in both cases (this is just to demonstrate the issue). Here is the code with the assignment inside the loop (which causes the malloc error):
NSArray *subArray;
//subArray = [wordDictionary subarrayWithRange:(NSRange){50000,20000}];
for (int i=0;i<200000;i++)
{
subArray = [wordDictionary subarrayWithRange:(NSRange){50000,20000}];
testBool = [subArray containsObject:#"hello"];
}
NSLog(#"Done");
The code above works if the sub-array assignment is moved outside the loop (as shown by the commented line)
In the error message I get the following is included:
* mach_vm_map(size=8388608) failed (error code=3)
error: can't allocate region
Terminating app due to uncaught exception 'NSMallocException'
reason: '* NSAllocateObject(): attempt to allocate object of class '__NSArrayI' failed'
libc++abi.dylib: terminating with uncaught exception of type NSException
Any hints as to what could be causing this and how to fix it are welcome!! Thanks!!
My guess is that you run out of memory. Every time you call -subarrayWithRange you allocate some memory (depends on the implementation, but could be 20000 * something).
Modern Objective-C uses automatic reference counting instead of garbage collection. That means memory does not get freed instantly, even though you assign another object to subArray.
Try moving it inside a local autorelease pool:
for (int i=0;i<200000;i++)
{
#autoreleasepool {
// your code
}
}
Even if you follow the advice given and create an autoreleasepool, this is awfully inefficient. You extract a range of 20,000 objects each time, which means 20,000 objects will be retained and later released, for no good reason at all. It's so easily avoided:
for (int i=0;i<200000;i++)
{
NSRange range = NSMakeRange (50000,20000);
testBool = [wordDictionary indexOfObject:#"hello" inRange:range] != NSNotFound;
}
NSLog(#"Done");
You might also consider whether an NSSet or NSOrderedSet wouldn't be the right data structure.

how to chain methods in objective-c

I'm trying to implement a parsing method to turn an XML document into a multi-dimensional array, however it relies on needing to removal objects from an array just after adding that array to the multi-dimensional array, like so:
while (k<blockRowArray.count){ //loops through all rows one by one
NSLog(#"current k is %i", k);
GDataXMLDocument *currentRow = (GDataXMLDocument *) [blockRowArray objectAtIndex:k];
NSArray *arrayOfBlocks = [currentRow nodesForXPath:#"b" error:nil];
j = 0;
while (j <arrayOfBlocks.count) {
NSLog(#"current j is %i",j);
GDataXMLElement *blockElement = (GDataXMLElement *) [arrayOfBlocks objectAtIndex:j];
NSNumber* blockValue = [NSNumber numberWithInt:[[blockElement stringValue] intValue]];
[individualRowOfBlocks addObject:blockValue];
j++;
}
k++;
NSLog (#"Current row of blocks array is %#",individualRowOfBlocks);
[rowBlocks addObject:individualRowOfBlocks];
[individualRowOfBlocks removeAllObjects];
}
However [individualRowOfBlocks removeAllObjects] is clearly running at the same time or before [rowBlocks addObject:individualRowOfBlocks] as I end up with a multi-dimensional array with a set of empty arrays in it, so what I need to do is make sure that [individualRowOfBlocks removeAllObjects] runs after [individualRowOfBlocks removeAllObjects] any methods of doing this?
However [individualRowOfBlocks removeAllObjects] is clearly running at
the same time or before [rowBlocks addObject:individualRowOfBlocks] as
I end up with a multi-dimensional array with a set of empty arrays in
it, so what I need to do is make sure that [individualRowOfBlocks
removeAllObjects] runs after [individualRowOfBlocks removeAllObjects]
any methods of doing this?
Unless there are threads (or queues) involved, Objective-C methods are always going to be executed in the order written in the code unless the methods being called explicitly do stuff in threads/queues in their implementation (which is exceptionally unlikely in that code).
Every time you call [rowBlocks addObject:individualRowOfBlocks];, you are adding a new reference to the same individualRowOfBlocks array. If you subsequently change the contents of individualRowOfBlocks, every slot in rowBlocks will effectively reflect that change because every slot points to the same instance of the array.
It sounds like instead of removeAllObjects, you should create a new instance of NSMutableArray on each pass through the loop, assigning it to individualRowOfBlocks.

NSarray count throwing out of bounds

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
}

objectAtIndex returns SIGABRT error

I have this line:
NSString *objectkey=[NSString stringWithFormat:#"%#",[line objectAtIndex:1]];
If objectAtIndex:0, it works fine. But if 1, it produces SIGABRT error at runtime.
However, I have another line to confirm that the array "line" has an object at index 1:
NSLog(#"%d",[line count]);
This returns 2 to the console.
Why would SIGABRT occur even though an index should exist there?
As for line, it is created like this:
for (int i=1;i<=[components count];i++){
NSArray*line=[[components objectAtIndex:(i-1)] componentsSeparatedByString:#"|"];
"line" is recreated during each loop iteration (I assume this is okay? no release is necessary, from what i understand using the "separated by string" method).
the array "components" contains lines such as:
Recipe Books|BOOKS
Recipe Photos|PHOTOS
I have created this little loop to verify that all are strings in "line":
for( NSObject* obj in line )
{
NSLog(#"%#",obj);
if ([obj isKindOfClass:[NSString class]]==YES) { NSLog(#"string"); }
}
Most likely the object contained at [line objectAtIndex:1] is not an NSString*. Why don't you try iterating over the set of objects in line and outputting them with NSLog. My guess is that the second one is going to print an address (of the form <Classname: 0x0>, not a string.
for( NSObject* obj in line )
{
NSLog(#"%#",obj);
}
Add an NSLog to the code where you create line:
for (int i=1;i<=[components count];i++){
NSLog(#"%#",[components objectAtIndex:(i-1)]);
NSArray*line=[[components objectAtIndex:(i-1)] componentsSeparatedByString:#"|"];
I have a strong feeling that your input data is bad, as you've excluded our prior answers as the solution with your responses. Your input data might have a <cr> somewhere it doesn't belong or missing data. How big is the input file for components?
Second answer based on gdb results
Try using [NSString stringWithString:[line objectAtIndex:1]] instead of stringWithFormat. Based on your use of gdb its likely that stringWithFormat is breaking on the unexpected control character (and trying to format it). stringWithString should copy the string character by character. Removing the control character(s) is another problem. :)
Assuming there is a valid object at index 1 of your line NSArray, the likely answer is that it's not a NSString, it's some other class that can't be translated using %# in your stringWithFormat: call. Look at the stack where it aborts and you could also check the type of the object before calling the stringWithFormat: call.

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).