hoping for advice on something.
I have a Levels Engine class that creates an NSMutable Array called levelsArray.
I am passing the data to a Levels View Controller which is working just fine.
I also have a Particle Emitter class to which I am hoping to pass the level data.
However I am constantly being told that the count level of the array is 0 when I pass it to the Particle Emitter class.
The array has been setup properly:
**LevelsEngine.h**
#interface
LevelsEngine : NSObject {
NSMutableArray *levelsArray; }
#property (retain) NSMutableArray
*levelsArray;
**LevelsEngine.m**
#synthesize levelsArray;
LevelsArray =[NSMutableArray array];
**Code used in ParticleEmitter.m**
newlevelsArray = [NSMutableArray array];
newlevelsArray=view.levelsArray;
Am I right in thinking I am having this error because I am trying to pass the array data from one NSObject to another and not to a view controller?If so how can I pass the data?
Couple of things.
**Code used in ParticleEmitter.m**
newlevelsArray = [NSMutableArray array];
newlevelsArray=view.levelsArray;
The first line is creating a new array.
The 2nd line is assigning newlevelsArray to be a pointer to the array in view.levelsArray, leaving the object you created in line #1 orphaned.
I think you were intending the 2nd line to be a field by field copy of the array, but assignments of objects don't work that way.
You can fix this by 2 things.
1) Remove the first line newlevelsArray = [NSMutableArray array];
2) Change the 2nd line to `newlevelsArray = [view.levelsArray copy];
This will actually do a copy, which is probably what you want since you can then go ahead and modify newlevelsArray in ParticleEmitter.m without changing the value in view.
Important note: don't forget to create a -dealloc: method in your Particle emitter class which releases newlevelsArray:
-(void)dealloc {
if (newlevelsArray) [newlevelsArray release];
[super dealloc];
}
An alternative solution is to use setters.
Instead of:
2) Change the 2nd line to newlevelsArray = [view.levelsArray copy];
Do:
2) Change the 2nd line to this.newlevelsArray = view.levelsArray;
Where you have to define newlevelsArray to be a property of the ParticleEmitter class using
#property (copy) NSMutableArray * newlevelsArray;
Note the use of "copy" instead of "retain". This will do a field by field copy of the array, which is most likely advisable for containers of mutable objects.
You need to change your code,
call the newlevelarray in the LevelsEngine.h calls.
and your code should look like
Classobject.newlevelsArray =[nsarray arraywitharray: LevlesArray] ;
This should solve your problem.
Related
General noob questions:
(1) How can I create an NSMutable array in a buttonClicked action that I can add more entries to during subsequent clicks of the same button? I always seem to start over with a new array at every click (the array prints with only 1 entry which is the most recent button's tag in an NSLog statement).
I have about 100 buttons (one for each character in my string called "list") generated by a for-loop earlier in my code, and each has been assigned a tag. They are in a scrollview within the view of my ViewController.
I wish to keep track of how many (and which ones) of the buttons have been clicked with the option of removing those entries if they are clicked a second time.
This is what I have so far:
-(void) buttonClicked:(UIButton *)sender
NSMutableArray * theseButtonsHaveBeenClicked = [[NSMutableArray alloc] initWithCapacity: list.length];
NSNumber *sendNum = [NSNumber numberWithInt:sender.tag];
[theseButtonsHaveBeenClicked addObject:sendNum at index:sender.tag];
NSLog(#"%#",theseButtonsHaveBeenClicked);
}
(2) I have read that I may be able to use a plist dictionary but I don't really understand how I would accomplish that in code since I cant type out the items in the dictionary manually (since I don't know which buttons the user will click). Would this be easier if I somehow loaded and replaced the dictionary in a plist file? And how would I do that?
(3) I also have no idea how I should memory manage this since I need to keep updating the array. autorelease?
Thanks for any help you can provide!
Okay, firstly you are creating a locally scoped array that is being re-initialised on every call to buttonClicked:. The variable should be part of the class init cycle.
You will also be better off with an NSMutableDictionary instead of an NSMutableArray. With a dictionary we don't have to specify capacity and we can use the button's tags as dictionary keys.
Here's what you need to do, these three steps always go together: property/synthesize/release. A good one to remember.
//Add property declaration to .h file
#property (nonatomic, retain) NSMutableDictionary * theseButtonsHaveBeenClicked;
//Add the synthesize directive to the top of .m file
#synthesize theseButtonsHaveBeenClicked;
// Add release call to the dealloc method at the bottom of .m file
- (void) dealloc {
self.theseButtonsHaveBeenClicked = nil; // syntactically equiv to [theseButtonsHaveBeenClicked release] but also nulls the pointer
[super dealloc];
}
Next we create a storage object when the class instance is initialised. Add this to your class's init or viewDidLoad method.
self.theseButtonsHaveBeenClicked = [[NSMutableDictionary alloc] dictionary]; // convenience method for creating a dictionary
And your updated buttonClicked: method should look more like this.
-(void) buttonClicked:(UIButton *)sender {
NSNumber *senderTagAsNum = [NSNumber numberWithInt:sender.tag];
NSString *senderTagAsString = [[NSString alloc] initWithFormat:#"%#",senderTagAsNum];
// this block adds to dict on first click, removes if already in dict
if(![self.theseButtonsHaveBeenClicked objectForKey:senderTagAsString]) {
[self.theseButtonsHaveBeenClicked setValue:senderTagAsNum forKey:senderTagAsString];
} else {
[self.theseButtonsHaveBeenClicked removeObjectForKey:senderTagAsString]; }
[senderTagAsString release];
NSLog(#"%#", self.theseButtonsHaveBeenClicked);
}
I do some iOS programming stuff and I have a UIViewController with a NSMutableArray:
#property (nonatomic, retain) NSMutableArray* mutableTestArray;
...
#synthesize mutableTestArray;
In viewDidLoad I want to call a method which is inside the implementation of this UIViewController:
//- (void)aTestMethod:(NSMutableArray *)myMutableTestArray;
[self aTestMethod:self.mutableTestArray];
So I call the method with a NSMutableArray which is an instance variable of the UIViewController. Inside this method, I do this:
myMutableTestArray = [NSMutableArray arrayWithCapacity:100];
//... looping & generating some objects and adding them to the array:
[myMutableTestArray adObject:myObject];
Now, I debug it and inside the method myMutableTestArray is fine. There are all objects inside the array. But leaving this method the instance variable mutableTestArray is empty.
So, where is the problem? Anyone an idea?
Note: I know I can access the instance variable with self.mutableTestArray and then everything will be okay, instead using it as a parameter, but I want to know what's wrong with my code.
Thank you in advance & Best Regards.
Parameters are passed by value in Objective-C. Thus, you are creating a copy of a pointer to the object and passing that into the method. When you do myParam = ... new object ...; that resets the copy to point to a new location, but has no effect on the original copy in the caller.
(To reiterate -- you are copying the pointer, not copying the object.)
To solve, declare your test method as returning an object:
- (NSMutableArray *)aTestMethod;
Then, you can simply:
self.mutableTestArray = [whateverObject aTestMethod];
(Since you aren't actually using the value passed in in the first place, there is no need for a parameter at all).
By your command
myMutableTestArray = [NSMutableArray arrayWithCapacity:100];
you are creating new allocation of mutableTestArray. So passing mutableTestArray as parameter to aTestMethod:
[self aTestMethod:self.mutableTestArray];
is useless because you override its value immediately when create this array inside your aTestMethod.
Try to create your array before passing it to your method (where you will fill it with data).
Remember that parameters are transmitted by value, so in aTestMethod: you are modifying not the original pointer but a copy of it!
For this to work, you should pass the address of the pointer as in
self aTestMethod:&(self.mutableArray)
then the prototype of the method should be
-(void)aTestMethod:(NSMutableArray **)myArray
and in the code of it you should used *myArray as in
*myArray = [[NSMutableArray alloc] init];
Yours, JB
I just did a search for my particular question and although the answers are close, I can't get my head around the answer...so I need some assistance.
I'd like to populate an array (NSMutableArray I suppose) with a group of dictionary objects that are parsed from JSON strings...the dictionary part I got, the JSON parsing I got, but when I try to put these objects into the NSMutableArray and NSLog it I get (null)... here are my steps (in a general way)
edit 1:
-The array I am creating is called NewFinalArray. it is an NSMutableArray, declared at the .h file and synthesized (and now alloc'd and init'd) as noted in the viewDidLoad method of the DetailViewController. It's contents are to be displayed in a UITableView.
-In DetailViewController, I have been successful in creating a plain NSArray/NSMutableArray and populating it with values that display in my UITableView.
-In the new scenario, I am receiving the information to be displayed through JSON strings which are retrievable through dictionary objects. I am using the Stig JSON libraries for iPHone/iPad. I have no problems there.
-All I wanted to do is getting the existing dictionary objects (which I can loop through from the existing array and see) and add them to a new Array to be used for displaying menu items in my UITableview.
I declared my mutableArray in my .h file
#interface blah : ...,...,...{
NSMutableArray *newFinalArray;
// other vars and IBOutlets
}
#property (nonatomic, retain) NSMutableArray *newFinalArray;
// other #property and (IBAction) stuff
#end
I then synthesize it in my .m file... I even alloc/inited it at viewDidLoad (it's a DetailViewController)
#synthesize this,that, newFinalArray; // keep track of newFinalArray, that's the one I want
- (void)viewDidLoad {
// other code
[[newFinalArray alloc] init]; // ya returns a warning, about not responding to alloc, but whatever (for now)
// I also tested of course without having to do that.
in my method that uses newFinalArray, the method is a recursive function that calls itself. each time it calls, it should add the dictionary object to the array (or does it?)
-(void)digTree:(NSArray *)array{
for (NSDictionary *dictionary in array){
// looping through the array
[self newFinalArray addObject:[dictionary]];
// more other code, and somewhere along the way I recurse
[self digTree:anotherArray];
}
}
when I try to NSLog (#"my final array is %#", newFinalArray) I get (null).
I am probably missing something here. I tried to add "nil" at the end. I am a little new/green to this , so if someone can lend a hand and let me know how to populate my newFinalArray with these dictionary objects it would be most appreciated.
[[newFinalArray alloc] init];
should be:
newFinalArray = [[NSMutableArray alloc] init];
This line is wrong too:
[self newFinalArray addObject:[dictionary]];
it should be:
[newFinalArray addObject:dictionary];
The first thing I notice that is wrong, is it should be:
newFinalArray = [[NSMutableArray alloc] init];
in viewDidLoad. See if that fixes it. It looks like there are other things wrong as well, so turn on warnings and see what else the compiler warns you about for hints.
How are the dictionaries stored? An alternative/probably easier way to do this would probably be to use arrayWithObjects:. Also, when using addObject:, there is no need to add nil (in fact, you can't add nil).
The data source for my table view is a plain NSMutableArray that will be populated as the app runs, but will be empty when the Table View first loads. The class interface looks like this...
#interface ViewController_iPhone : UITableViewController {
NSMutableArray *serverList;
}
#property (retain, readonly) NSMutableArray *serverList;
#end
My questions are...
Currently, I initialize it in the viewDidLoad method like so...
serverList = [[NSMutableArray alloc] initWithCapacity:1];
I do it this way because the array needs to be valid in order for my numberOfRowsInSection method to avoid crashing when reading the count of the array (which will be zero) when the view first loads. My current approach of using initWithCapacity just feels a little clunky since I just need an empty, but valid array object that will return a count value of zero when the view loads. How should I be initializing my serverList array?
While playing around, I noticed that when I try and initialize the serverList array this way...
serverList = [NSMutableArray arrayWithCapacity:1];
it crashes on that line. Why?
Thanks in advance for all your help!
Here's a key concept to learn about member variables and properties: Member variables are not the same as properties.
That is, when accessing a member variable in your class:
serverList = [NSArray array]; is not the same as self.serverList = [NSArray array];
serverList by itself means you're accessing it directly.
self.serverList means you're using the getter/setter methods to access it.
Normally this isn't that big of a deal when dealing with basic variable types. However, when your property uses retain or copy, that means your setter method will automatically retain it when you use it, but it won't do such when you access it directly.
That means:
serverList = [NSArray array]; will not retain the array.
self.serverList = [NSArray array]; will retain the array.
It should be noted that [NSMutableArray arrayWithCapacity:1]; (and 99% of other methods that aren't alloc) will return an object that is autoreleased. If you want to keep it for later use, as you need to in this case, then you must retain it in some form or fashion.
I somehow missed the simplest approach and found that when I simply create the array like so...
serverList = [[NSMutableArray alloc] init];
and release it in the dealloc method, everything works great!
Note that capacity does not mean any objects are in the array. Using -initWithCapacity: simply sets aside a chunk of space for the array. Nothing is in the array even with a non-zero capacity.
The following initializers:
self.serverList = [[[NSMutableArray alloc] init] autorelease];
self.serverList = [[[NSMutableArray alloc] initWithCapacity:capacity] autorelease];
self.serverList = [NSMutableArray array];
self.serverList = [NSMutableArray arrayWithCapacity:capacity];
should all work, however.
Make sure you specify self so that the property is accessed. Using serverList by itself will not work.
Make sure you autorelease any alloc-init of a property which you are retaining, otherwise you will have a memory leak.
You do not need an array with a specified capacity. This is just a performance convenience for setting aside contiguous memory, and useful if you have a rough idea how much space your array will need up-front. An array that is created through alloc-init will also be empty, with a count of zero.
Been searching for the answer to this for a while now and I think due to the nature of my array set up, I may be searching for the wrong answer!
I have a class which handles adding items to my array:
// Item.h
#interface Item : NSObject {
NSString *name;
NSNumber *seconds;
}
#property(nonatomic,copy) NSString *name;
#property(nonatomic,copy) NSNumber *seconds;
- (id)initWithName:(NSString *)n seconds:(NSNumber *)sec;
#end
and...
//item.m
#implementation Item
#synthesize name, seconds;
- (id)initWithName:(NSString *)n seconds:(NSNumber *)sec {
self.name = n;
self.seconds = sec;
return self;
}
#end
So to add an item, I use
Item *item1 = [[Item alloc] initWithName:#"runnerA" seconds:[NSNumber numberWithInt:780]];
I have some code which allows a user to edit a textfield (runner name) and the time which is a UIdatepicker set to hours and minutes. In the save method, that's working fine. It's the UPDATE that I cannot get to work. I've tried alsorts! Here's the code at the moment...
mainAppDelegate *appDelegate = (mainAppDelegate *)[[UIApplication sharedApplication] delegate];
Item *item = [[Item alloc] initWithName:inputName.text seconds:[NSNumber numberWithInt:secs]];
[appDelegate.arrItems replaceObjectAtIndex:rowBeingEdited withObject:item];
The above is simply adding a new item to the array (which is what I don't want). I'm not sure how to replace values. At the function, I have the row I need to update (rowBeingEdited) and the fields inputName.text and secs are both OK. (NSLog out confirms this).
How do I use the replaceObjectAtIndex to actually replace it with the values?! It's driving me mad now!!
Since you are simply trying to edit a particular row, why not use those property accessors that you already have set up in Item? It would look something like this:
Item *item = (Item *)[appDelegate.arrItems objectAtIndex:rowBeingEdited];
[item setName:inputName.text];
[item setSeconds:[NSNumber numberWithInt:secs]];
An a side note, are you using garbage collection, or do you manually release the Item objects that you create when adding items to the array? If you are doing it manually, it should look like this:
Item *item1 = [[Item alloc] initWithName:#"runnerA"
seconds:[NSNumber numberWithInt:780]];
[appDelegate.arrItems addObject:item1];
[item1 release];
This follows the rule of thumb: if you alloc, copy or retain anything, you must also release it. Note that this works because the array will retain the item when it is added.
Are you using NSArray or NSMutableArray?
Assuming you are using NSMutableArray, how did you initialize and populate the array in the first place?
For example, it's not enough to use -initWithCapacity: or +arrayWithCapacity: which only sets aside space. You have to use -addObject: for the first round of population, before you can use -replaceObjectAtIndex:withObject::
Note that NSArray objects are not like C arrays. That is, even though you specify a size when you create an array, the specified size is regarded as a “hint”; the actual size of the array is still 0. This means that you cannot insert an object at an index greater than the current count of an array. For example, if an array contains two objects, its size is 2, so you can add objects at indices 0, 1, or 2. Index 3 is illegal and out of bounds; if you try to add an object at index 3 (when the size of the array is 2), NSMutableArray raises an exception.