NCSFDictionary, Mutating method sent to immutable object - objective-c

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

Related

DFS algorithm implementation at Objective C

I am trying to implement the Objective C realization of this algorithm. Here the implementation of it:
#implementation DFSAlgorithm
-(void)dfs:(Graph*)g andStartingPosition:(int)s{
[self performDFS:g andPosition:s];
}
-(void)markedArrayInit:(int)capacity{
//0 is for unmarked vertices
//1 is form marked ones
self.marked=[[NSMutableArray alloc]initWithCapacity:capacity];
for(int i=0;i<[self.marked count];i++)
[self.marked replaceObjectAtIndex:i withObject:[NSNumber numberWithInt:0]];
}
-(void)performDFS:(Graph *)g andPosition:(int)v{
[self markedArrayInit:(int)[g numberOfVertices]];
[self.marked replaceObjectAtIndex:v withObject:[NSNumber numberWithInt:1]];
for (NSNumber *vertex in [g.vertices objectAtIndex:v]){
if(1==[self isMarked:v atGraph:g]){
NSLog(#"%d",(int)vertex);
[self performDFS:g andPosition:(int)vertex];
}
}
}
-(int)isMarked:(int)v atGraph:(Graph *)g{
return [self.marked objectAtIndex:v];
}
#end
However, I don't understand why the following error occurs:
[__NSArrayM replaceObjectAtIndex:withObject:]: index 0 beyond bounds for empty array'
How can I correctly initialize the marked array?
Thank you.
An NSMutableArray is created empty, the capacity value you pass is just a hint to the implementation about how large you expect the array to become.
Therefore replaceObjectAtIndex:withObject: does not work for you, as the array is empty you have no objects to replace.
Instead just use addObject: capacity times.
In your markedArrayInit method you create empty mutable array and reserve memory for it to hold at least capasity number of items. But you do not actually add anything to it (for loop in that method does not actually do anything at all). To fix your problem you can add enough number of items in for loop:
for (int i=0;i< initWithCapacity:capacity;i++)
[self.marked addObject: #0];
}
Edit:
Your implementation has several other problems:
you initialize marked array on each call to performDFS:andPosition:, and call that method recursively. You should move initialization to dfs:andStartingPosition: method
In isMarked:atGraph: method you return object from array, not the numeric value it holds - so it will never be 1, you might want to replace it with the following implementation (Note that method name implies we return some boolean value, not an integer that we'll need to interpret later):
-(BOOL)isMarked:(int)v atGraph:(Graph *)g {
return [self.marked[v] intValue] == 1;
}
...
if([self isMarked:v atGraph:g]){
...
}
There're better data structures to store indices of marked nodes, e.g. NSSet or NSIndexSet
You try to replace not existing object inside array. In markedArrayInit use addObject: method from NSMutableArray. [self.marked count] is always 0 in for cycle.

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.

What is the Difference b/w addObject:dictionary and addObject:[dictionary copy] to an NSMutableArray?

I'm trying to set some values to my NSMutableDictionary inside a loop and assigning the dictionary values to an NSMutableArray each time like below,
for(int i=0;i<=lastObj;i++){
[mutableDict setValue:[valuesArray objectAtIndex:i] forKey:#"Name"];
[mutableDict setValue:[uniqueArray objectAtIndex:i] forKey:#"SSN"];
//......then
**[mutableArray addObject:mutableDict];**/*not working perfectly all values s replaced by final mutableDict values (duplicate values)*/
but
**[mutableArray addObject:[mutableDict copy]];**/* Working Correctly !!! */
}
inside loop in each iteration new values is assigned to mutableDictionary and whenever i say just addObject my mutableArray is getting all duplicate values but whenever i say addObject:[mutableDict copy], array is getting correct unique values without any duplicates, i don't know what the difference the compiler feels when i say copy, can anybody tell me the difference in this.
Thanks in advance.
[mutableArray addObject:mutableDict];
keeps on adding mutableDict to mutableArray. adding the object doesnt create a new memory location, it points to the same memory. so when u set the values of mutableDict, it gets reflected in all added objects as they refer to same memory location.
copy creates a new memory location and adds the object removing the above scenario.
to avoid this u can add
for(int i=0;i<=lastObj;i++){
NSMutableDictionary * mutableDict = [NSMutableDictionary new]; //create new memory location
[mutableDict setValue:[valuesArray objectAtIndex:i] forKey:#"Name"];
[mutableDict setValue:[uniqueArray objectAtIndex:i] forKey:#"SSN"];
[mutableArray addObject:mutableDict];
[mutableDict release]; //release
}
hope this helps u. happy coding :)
Looking at your code block, its clear you are allocating mutableDict outside your for loop. Hence When you say below statement inside the for loop, basically you are passing the same object for addition to mutalbeArray.
[mutableArray addObject:mutableDict];
addObject doesnt allocate any memory for objects, but it uses the same object passed and just sends "retain" message to increase the reference count. Since you are passing the same object within the for loop, mutalbeArray would contain the references to same object.
In case of copy say,
[mutableArray addObject:[mutableDict copy]];
the copy message sent to mutableDict returns a new copy which is then passed addObject. Hence every iteration in the forloop passes a new object(due to copy) to addObject.
Also note these following
Copy creates a new object, it should be later released, but your code is not releasing it. This will lead to memory leak.
Copy creates immutable object, hence you should probably use mutableCopy instead if you want objects added to mutableArray to be modified.

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.

My NSMutableArray doesn't work

sorry for my stupid question (beginner)
I got the demo program Accelerometergraph the apple site and would like to use
NSMutableArray in the values of acceleration x.
but my NSMutableArray contains only one object, there being several passage
NSMutableArray routine and should contain the same number of objects that the counter
show, how code below
if(!isPaused)
{
array = [[NSMutableArray alloc] init];
[filter addAcceleration:acceleration];
[unfiltered addX:acceleration.x y:acceleration.y z:acceleration.z];
NSNumber *number = [[NSNumber alloc] initWithDouble:acceleration.x];
[array addObject:number];
++a;
if (a == 30) // only check the # objs of mutablearray
{
sleep(2);
}
[filtered addX:filter.x y:filter.y z:filter.z];
}
It looks like you're missing a loop of some kind. The code you list above:
Makes sure something isn't paused.
Creates a new (empty) mutable array.
Adds a value to the new array.
And does some other work.
My guess is that this whole if{} block sits inside some kind of loop. You need to alloc and init the mutable array outside of the loop instead.
You create a new array each time the if block is entered, so the addObject: will only add the object to the most recently created array.
Furthermore, you are leaking the array and number objects. Each time you allocate an object, you are responsible for releasing it. Make sure you're familiar with the guidelines set out in the memory management programming guide.