Objective C: Why doesn't my NSMutableArray work? - objective-c

I'm quite new to ObjC and its mutable arrays. This is driving me crazy ;-)
I do the following:
mColumns = [NSMutableArray array];
for( int i=0; i<5; i++ ) [mColumns addObject:[[MyColumn alloc] init]];
After that code my array "mColumns" contains 5 elements. But each of them is a NULL-Pointer! (Or at least that's what the debugger tells me).
I already checked that the code
[[MyColumn alloc] init]
Gives me some valid objects, so I have no idea why my array gets filled with 0x0s.
Can you give me a hint?

Retain your mutableArray if you want it to stick around. At the end of the current event-loop it will be automatically deallocated, as it is in the autoreleasePool.
At that point all bets are off. Your mColumns variable just points to a junk piece of memory, maybe another object, maybe half an object, maybe even you can still get the correct count or even a contained object, but at some point your app will crash.
Just a quick point, if [[mColumns objectAtIndex:x] addObject:g]; crashes your app is it [mColumns objectAtIndex:x] that is causing the crash or is it addObject:g ?
Why not put them on separate lines and find out?

I know you say that the objects are allocated correctly but I'd be inclined to check it. Here's the same code with more debugging:
NSMutableArray *mColumns = [NSMutableArray array];
for( int i=0; i<5; i++ ) {
MyColumn *col = [[MyColumn alloc] init];
NSLog(#"%d: %#", i, col);
[mColumns addObject:col];
[col release];
}
NSLog(#"Array has %d elements, first is %#", [mColumns count], [mColumns objectAtIndex:0]);
The problem is likely to be either (a) the object is not being created correctly, or (b) the array does have the elements and you have a different problem.

Hmmm. I can see one problem in your code, but not one that would cause this issue: you are adding retained objects to the array; you'll want to autorelease those first so you don't leak.
As for your actual problem, I don't know. It's impossible to fill an array with null pointers; only objects of type id (not arbitrary pointers) can be put inside of an NSArray/NSMutableArray.
I know you said that you have checked -[MyColumn init], but it would be a good idea to check that it is producing the proper objects in this very spot.
columns = [NSMutableArray array];
for (int i = 0; i < 5; i++) {
id c = [[[MyColumn alloc] init] autorelease];
/* set a breakpoint here, and type `po c`
into the debugger to see what was created
*/
[columns addObject:c];
}
Unless -[MyColumn init] is making some really funky objects, I can't figure out what the problem would be. I wonder whether something weird is happening to mColumns; is it retained by anything, for instance?

Related

arrays are not adding to the nsmutabledictionary

i am adding the arrays to an nsmutabledictionary but its giving 0 count
i allocated the dictionary in viewDidLoad method.
can any body help me out please..
thanks.
- (void)creatingDictionary{
songsDict = nil;
NSMutableArray *hrSongs = [[NSMutableArray alloc]init];
for(int i=0;i<[onAirSongs count];i++){
for(int j=i;j<[onAirSongs count];j++){
Song *a1 =(Song *)[onAirSongs objectAtIndex:i];
Song *a2 =(Song *)[onAirSongs objectAtIndex:j];
if(a1.cellId == a2.cellId)
[hrSongs addObject:a2];
else{
[songsDict setObject:hrSongs forKey:[NSString stringWithFormat:#"%d",i]];
hrSongs = nil;
i=j-1;
break;
}
}//inner for
}//out for
[hrSongs release];
NSLog(#"songsdictionary count....%d",[songsDict count]);
}
You're setting songsDict to nil. You can't add an array to a dictionary that doesn't exist.
blindJesse is correct, but there are more problems here:
You are setting songsDict to nil. In Objective-C, nil-eats-messages and, thus, you are silently not doing anything at all when you [songsDict setObject:.....].
In the middle of the loop, you hrSongs = nil;. Subsequent iterations through the loop will happily call addObject: on nil, doing nothing at all.
This whole thing seems kinda weird; what does onAirSongs contain? Cells? This seems like you haven't really preserved any kind of Model-View-Controller separation. It might work, but you are in for a severe maintenance headache down the road if you ever have to change your UI.

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

why the tableview doesn't show binding data?

Here's my code of generating data
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
[array initWithCapacity:20];
}
- (IBAction) readlog:(id)sender {
for (int i = 0; i < 20; i++) {
NSDictionary *d = [NSDictionary dictionaryWithContentsOfFile:[path stringByAppendingFormat:#"/%d.log",i]];
[array addObject:d];
}
}
- (IBAction) writelog:(id)sender {
for (int i = 0; i < 20; i++) {
NSMutableDictionary *d = [NSMutableDictionary dictionary];
NSString *name = [NSString stringWithFormat:#"testfile%d", i];
[d setObject:[NSDate date] forKey:#"date"];
[d setObject:[path stringByAppendingFormat:#"/%d.log", i] forKey:#"path"];
[d setObject:name forKey:#"name"];
[d writeToFile:[path stringByAppendingFormat:#"/%d.log", i] atomically:YES];
}
and I bind my tableview column with appdelegate.array with keypath name/path/date
but it doesn't show any data in the array.. is there anything wrong here?
Thanks!
You haven't created an array.
init methods, including NSMutableArray's initWithCapacity:, initialize an existing (freshly-created) instance. You haven't created one, so you're sending that initWithCapacity: message to nil, which means it has no effect.
You need to create the array, then initialize it, then assign it to your array variable, preferably all in the same line.
There's also the issue that your table view will have already asked for the array by the time you receive the applicationDidFinishLaunching: message. You don't have one yet, so it gets nothing; by the time you create one, it has already asked you for it and gotten its answer, and does not know that it should ask again.
Create your array in init or initWithCoder: (I believe you will need the latter if your app delegate is in a nib), and implement and use Key-Value-Coding-compatible accessor methods to fill the array with values. When you send yourself accessor messages, you'll cause KVO notifications that will tip off the table view that it needs to ask for the array again. Assigning directly to the instance variable will not cause this effect.
A couple of other thingsā€¦
You have three [path stringByAppendingFormat:#"/%d.log", i] expressions in two different methods. Don't repeat yourself. Move that to a method named something like logFileNameWithLogFileNumber: and send yourself that message to generate the filename. This will make the code both clearer and easier to maintain.
Finally, as a matter of style, you should not use stringByAppendingFormat: or stringWithFormat: to construct paths. Use stringByAppendingPathComponent: (in this case, together with stringWithFormat: to generate the filename). Clarity and pathname-separator-independence are virtues.

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.

Reusing NSMutableArray

I'm getting some leaks (obvserved by Instruments) when trying to reuse an existing NSMutableArray (in order to save memory).
Basically I'm creating an NSMutableArray, filling it with objects (UIImages) and passing it onto another object which retains it. However, I now need to use an NSMutableArray again. I figured I would release all its objects, empty it, and everything would be fine, but Instruments reports a CALayer leaked object (??) from that very method which looks something as follows:
NSString *fileName;
NSMutableArray *arrayOfImages = [[NSMutableArray alloc] init];
// fill the array with images
for(int i = 0; i <= 7; i++) {
fileName = [NSString stringWithFormat:#"myImage_%d.png", i];
[arrayOfImages addObject:[UIImage imageNamed:fileName]];
}
// create a button with the array
aButton = [[CustomButtonClass buttonWithType:UIButtonTypeCustom]
initWithFrame:someFrame
imageArray:arrayOfImages];
// release its objects
for(int i = 0; i < [arrayOfImages count]; i++) {
[[arrayOfImages objectAtIndex:i] release];
}
// empty array
[arrayOfImages removeAllObjects];
// fill it with other images
for(int i = 0; i <= 7; i++) {
fileName = [NSString stringWithFormat:#"myOtherImage_%d.png", i];
[arrayOfImages addObject:[UIImage imageNamed:fileName]];
}
// create another button with other images (same array)
aSecondButton = [[CustomButtonClass buttonWithType:UIButtonTypeCustom]
initWithFrame:someFrame
imageArray:arrayOfImages];
[arrayOfImages release];
For the sake of clarity, my button init method looks as follows:
- (id)initWithFrame:(CGRect)frame
images:(NSArray *)imageArray
{
if(self = [super initWithFrame:frame]) {
myImageArray = [[NSArray arrayWithArray:imageArray] retain];
}
return self;
}
I know I could just create a new NSMutableArray and be over with this issue but it annoys me not to be able to just reuse the old array. What could be the problem?
I'm getting some leaks (obvserved by
Instruments) when trying to reuse an
existing NSMutableArray (in order to
save memory).
An array takes a really small amount of memory; 4 bytes per pointer stored (on a 32 bit system) + a tiny bit of overhead. Reusing an array to attempt to save memory is a waste of time in all but the most extraordinary circumstances.
// release its objects
for(int i = 0; i < [arrayOfImages count]; i++) {
[[arrayOfImages objectAtIndex:i] release];
}
// empty array
[arrayOfImages removeAllObjects];
You didn't retain the objects and, thus, you shouldn't be releasing them! That your app didn't crash after the above indicates that you are likely over-retaining the objects somewhere else.
I know I could just create a new
NSMutableArray and be over with this
issue but it annoys me not to be able
to just reuse the old array. What
could be the problem?
There isn't anything in that code that springs out as a memory leak. Just the opposite; you are over-releasing objects.
And the above indicates that you really need to revisit the memory management guidelines as re-using an array versus releasing the array and creating a new one really doesn't have anything to do with this problem.
You don't need this part:
// release its objects
for(int i = 0; i < [arrayOfImages count]; i++) {
[[arrayOfImages objectAtIndex:i] release];
}
This is against the ownership rule. You didn't retain the images at
[arrayOfImages addObject:[UIImage imageNamed:fileName]];
so it's not your responsibility to release them. It's NSMutableArray that retains them when -addObject is called, and so it's NSMutableArray's responsibility to release them when -removeObject or others of that ilk is called. The leak you found might be because the system got confused by this over-releasing...
I would also recommend to perform "Build and Analyze" in XCode.
The Leaks instrument tells you where the object that leaked was first allocated. The fact one of the images was leaked means that you used the image somewhere else, and did not release it there - Leaks cannot tell you where since it cannot show you code that does not exist.
Indeed as others have pointed out this is somewhat surprising since the code as-is is over-releasing objects and should have crashed. But the fact it did not crash is a good sign somewhere else you are using the images from the array and over-retaining them.