PDA

View Full Version : nsmutablearray problem




medasmx
Jul 13, 2011, 10:59 PM
interface file…


IBOutlet UILabel*label;
NSMutableArray*myArray;
IBOutlet UITextField*someText;
}
-(IBAction)programLabel:(id)sender;
-(IBAction)addText:(id)sender;


implementation file...


- (id)init {
if (self == [super init]) {
myArray = [[NSMutableArray alloc] initWithObjects: nil];
myArray = [NSMutableArray arrayWithCapacity:50];
}
return self;
}

-(IBAction)programLabel:(id)sender{

label.text=[NSString stringWithFormat:@"%@",[myArray objectAtIndex:0]];
}

-(IBAction)addText:(id)sender{

[myArray insertObject:[NSString stringWithFormat:@"%@", someText.text] atIndex:[myArray count]];

NSLog(@"%@",[myArray objectAtIndex:0]);
}


Logging the contents of the mutableArray, myArray, it is null. If you initialize the mutable array within the action "addText" the array is not null, but then it cannot be used in the action "programLabel". As a second exercise, I tried to create a second mutable array locally (within addText), then adding its contents to myArray, using arrayWithArray, etc, but that hasn't worked either. I have google'ed extensively, and have been surprised not to find a sample addressing this online or in books, because it seems like it would be used commonly. In any case, any help is appreciated.



admanimal
Jul 13, 2011, 11:27 PM
The problem is the way in which you are creating the array, and the fact that you probably need to read up on memory management in Objective-C.

One of these two lines in your init is redundant:


myArray = [[NSMutableArray alloc] initWithObjects: nil];
myArray = [NSMutableArray arrayWithCapacity:50];


because both of them return a new array. You can use either one, but there is an essential difference between them when it comes to memory management that you must understand.

The first line gives you an array that your class now owns with a retain count of 1. That means this array will be valid as long as your class is, assuming that you release it in your class' dealloc method and not somewhere earlier.

The second line gives you an autoreleased array that you must retain if you want it to exist as long as your class does. Since you do not do this currently, myArray gets deallocated some time after init returns and before you have a chance to do anything else with the array.

If terms like retain, release, and autorelease aren't familiar to you, you should read the memory management in Objective-C guide in Apple's docs and/or other web tutorials on the subject.

jiminaus
Jul 14, 2011, 12:27 AM
- (id)init {
if (self == [super init]) {
myArray = [[NSMutableArray alloc] initWithObjects: nil];
myArray = [NSMutableArray arrayWithCapacity:50];
}
return self;
}

In addition to the reply above, there's a coding error hightlighted in red above. It should be =, not ==.

admanimal
Jul 14, 2011, 01:06 AM
In addition to the reply above, there's a coding error hightlighted in red above. It should be =, not ==.

Good catch!

jnoxx
Jul 14, 2011, 02:12 AM
In addition to the reply above, there's a coding error hightlighted in red above. It should be =, not ==.

Since Xcode 4.x it gives warnings when you do that, and suggests to do == instead of =.
Just saying what I have noticed ;p

jiminaus
Jul 14, 2011, 02:43 AM
Good catch!

Skills developed while tutoring (being a TA) to 1st year students, and from coding MARC library catalogue records. :)


Since Xcode 4.x it gives warnings when you do that, and suggests to do == instead of =.
Just saying what I have noticed ;p

The idiom should be either:
self = [super init];
if (self) {
// ...
}
return self;
or (notice the double parentheses):
if ((self = [super init])) {
// ...
}
return self;

jnoxx
Jul 14, 2011, 03:36 AM
The idiom should be either:
self = [super init];
if (self) {
// ...
}
return self;
or (notice the double parentheses):
if ((self = [super init])) {
// ...
}
return self;


Myself, I use the first one, Have used the second too.
Can't recall when Xcode is trying to replace the = with ==, i'll try to figure it out agian and let you know x)

admanimal
Jul 14, 2011, 12:53 PM
Myself, I use the first one, Have used the second too.
Can't recall when Xcode is trying to replace the = with ==, i'll try to figure it out agian and let you know x)

Well, Xcode knows that most of the time, == is in fact the appropriate operator to use in the conditional part of an if statement rather than =. It will give you that warning if you use = without putting the whole assignment in ().

medasmx
Jul 14, 2011, 05:45 PM
interface…I don't use NSMutableArray as a property


NSMutableArray*myArray;


implementation…


- (id)init {
self=[super init];
myArray=[[NSMutableArray alloc]init];
return self;
}

-(IBAction)programLabel:(id)sender{
NSLog(@"%@",[myArray objectAtIndex:0]);
label.text=[NSString stringWithFormat:@"%@",[myArray objectAtIndex:0]];
}

-(IBAction)addText:(id)sender{

NSString*someString=[NSString stringWithFormat:@"%@",someText.text];

myArray=[[NSMutableArray alloc]initWithObjects:someString,nil];

NSLog(@"%@",someString);
NSLog(@"%@",[myArray objectAtIndex:0]);
}


What I was looking for was the following. Declare a mutableArray in the interface, then input from the text file and add to that array in the implementation. Finally, display different elements from the array as labels. The downfall with the above is that I would like to be adding to the array (myArray) each time. What I have is creating an array with one element. Will keep working…

Appreciate comments from late last night and earlier today. Thanks.

later post--

This also works…


-(IBAction)addText:(id)sender{

NSString*someString=[NSString stringWithFormat:@"%@",someText.text];
NSMutableArray*someArray=[[[NSMutableArray alloc]init]autorelease];
someArray=[NSMutableArray arrayWithArray:myArray];
[someArray addObject:someString];
NSLog(@"%@",[someArray objectAtIndex:0]);
}


I then want to make myArray empty, and set it equal to a copy of someArray. When I tried that it didn't work. Thanks again for any advice.

admanimal
Jul 14, 2011, 08:48 PM
You are still creating waaay too many arrays. You should only need to initialize the array in your class' init method. Your second version of addText also still has the same problem as your original init method where you are redundantly creating the array twice in a row, and not doing appropriate memory management with either.

I would suggest stepping back and reading some more basic Objective-C tutorials and/or examples.

jiminaus
Jul 14, 2011, 09:03 PM
- (id)init {
self=[super init];
myArray=[[NSMutableArray alloc]init];
return self;
}

It's a bad habit not to check self before attempting to initialize instance variables. I won't get into to it, just use one of the two idioms I posted above.

label.text=[NSString stringWithFormat:@"%@",[myArray objectAtIndex:0]];
(and)
NSString*someString=[NSString stringWithFormat:@"%@",someText.text];

Are you using this technique as an explicit defensive programming technique? Or do you not understand what this codes does?


-(IBAction)addText:(id)sender{

NSString*someString=[NSString stringWithFormat:@"%@",someText.text];
NSMutableArray*someArray=[[[NSMutableArray alloc]init]autorelease];
someArray=[NSMutableArray arrayWithArray:myArray];
[someArray addObject:someString];
NSLog(@"%@",[someArray objectAtIndex:0]);
}


This is incredibly inefficient. You start by creating a new string by copying an existing string (may be valid, but I doubt you've done it deliberately). Then you create an empty mutable array, which you promptly discard in the next line. Then you copy an existing mutable array, add an object to it, and then forget it.

All you needed was:

-(IBAction)addText:(id)sender{
[myArray addObject:someText.text];
}


It really seems you have little understanding of what you're actually doing. I would strongly advise going back to basics and (re-)learning fundamental programming generally, and object-oriented programming more specifically.

Are you reading (or have you read) any books on Objective-C? If not, what resources are you using (or have you used)?

admanimal
Jul 15, 2011, 11:35 AM
As an additional tip, please please please put a space between a variable's type and its name when you are declaring it.

Seeing


UILabel*label;


will make most programmers want to scream.

Either of these two is acceptable


UILabel *label;
UILabel* label;


although the first is usually preferred because if you are declaring several pointer (i.e. *) variables in one statement, it -must- be done like this:


UILabel *label1, *label2, *label3;


where there is one * for each variable.

Icy1007
Jul 16, 2011, 12:52 AM
As an additional tip, please please please put a space between a variable's type and its name when you are declaring it.

Seeing


UILabel*label;


will make most programmers want to scream.

Either of these two is acceptable


UILabel *label;
UILabel* label;


although the first is usually preferred because if you are declaring several pointer (i.e. *) variables in one statement, it -must- be done like this:


UILabel *label1, *label2, *label3;


where there is one * for each variable.

THANK YOU! Glad someone said something.