Why does this work:
self.array = newArray;
But this doesn't:
[[self mutableArray] addObject:object];
Meaning, why do I need to init the NSMutableArray for it to work when I don't have to init the NSArray?
EDIT: Hops this is clearer guys.
Interface:
#interface Foo : UIViewController {
}
#property (nonatomic, retain) NSArray *array;
#property (nonatomic, retain) NSMutableArray *mutableArray;
#end
Implementation:
#implementation Foo
#synthesize array;
#synthesize mutableArray;
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [Class returningAnArray];
[[self mutableArray] addObject:objectIHaveOmittedTheCode];
}
#end
self.array = newArray;
In this line, you are assigning already created object to self.array. So, you've no need to create it.
[[self mutableArray] addObject:object];
But in this line you are trying to add an object to a array which is not created actually. If you don't create the array, it will be nil, and sending message to nil won't take any effect.
In Objective-C NSArray objects are immutable.
self.array = newArray;
This line is a property assignment. The self.array property returns a reference pointing to a location in memory that contains an NSArray object. By assigning the property to different objects you're not really modifying the object themselves.
If you wish to modify an existing NSArray object, you'll have to create a NSMutableArray object containing the same elements:
NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithArray:self.array];
[mutableArray addObject:object];
Note that NSMutableArray inherits from NSArray, so you can safely assign the object referenced by mutableArray to any variable of type NSArray.
[Class returningArray] did the allocation for you. Every object needs to be allocated (and should be initialized) before it can used. The solution is.
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [Class returningAnArray];
self.mutableArray = [NSMutableArray array];
//Now you can use mutable array
[[self mutableArray] addObject:objectIHaveOmittedTheCode];
}
Now you have created your array and your mutable array with out explicitly calling alloc on either because those classes have done it for you.
Related
I have a property an NSArray property called toolbarButtons that in one instance (when the UIViewController is modal) it needs to be an NSMutableArray, and when the UIViewController is nonmodal, it needs to simply be an NSArray.
I have initialized the property like this:
#property (nonatomic, copy) NSArray *toolbarButtons;
I am initializing toolbarButtons as follows:
- (void) setToolbarButtons
{
UIBarButtonItem *exitButton = [[UIBarButtonItem alloc] initWithTitle:#"Exit" style:UIBarButtonItemStyleBordered target:self action:#selector(exitButtonPushed)];
toolbarButtonsTemp = (NSMutableArray *)[[NSMutableArray alloc] init];
[(NSMutableArray*)toolbarButtonsTemp addObject:exitButton];
self.toolbarButtons = toolbarButtonsTemp;
[exitButton release];
}
Then later on I override the above method in a subclass like this:
- (void) setToolbarButtons
{
[super setToolbarButtons];
UIBarButtonItem *leftFlexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *toolButton = [[UIBarButtonItem alloc] initWithTitle:#"Tools" style:UIBarButtonItemStyleBordered target:self action:#selector(toolButtonPushed)];
[(NSMutableArray*)self.toolbarButtons addObject:leftFlexibleSpace];
[(NSMutableArray*)self.toolbarButtons addObject:toolButton];
[toolButton release];
[leftFlexibleSpace release];
}
and I'm getting the following error:
[__NSArrayI addObject:]: unrecognized selector sent to instance
I know there are a ton of work arounds to this, but from a design perspective I'm just wondering if what I'm doing is possible and how to correct my mistake.
#property ( nonatomic, copy ) NSArray* array
means:
- (void) setArray: (NSArray*) array
{
_array = [array copy];
}
[array copy] always returns an non mutable NSArray, even if array is a NSMutableArray.
#property ( nonatomic, copy ) NSMutableArray* array
is handled a bit differently:
- (void) setArray: (NSMutableArray*) array
{
_array = [array mutableCopy];
}
In that case, you can do
self.array = (NSMutableArray*) [NSArray new];
and array will be assigned with a NSMutableArray, as mutableCopy always returns an NSMutableArray.
The latter is of course strongly discouraged. It works for now. No guarantee it will work forever.
Your other choice, besides an NSMutableArray, is to use a strong property and to handle the copies yourself.
you cannot do that!
when you:
[(NSMutableArray*)self.toolbarButtons addObject:leftFlexibleSpace];
[(NSMutableArray*)self.toolbarButtons addObject:toolButton];
you are just casting the property, it means you are promising in runtime that that property is a NSMutableArray... but you are lying!!!
i suggest you do the contrary: just declare it as a NSMutableArray (it's a subclass of NSArray, so you can use it as a NSArray when you want it),and use the NSMutableArray methods just when you need them
Casting won't work in this case. You need to change your property to an NSMutableArray:
#property (nonatomic, copy) NSMutableArray *toolbarButtons;
You should just use an NSMutableArray in your property, but you could always do something like this too...
NSMutableArray *mutArray = [[NSMutableArray alloc] init];
[mutaArray addObject:leftFlexibleSpace];
[mutaArray addObject:toolButton];
self.toolbarButtons = [NSArray arrayWithArray:mutArray];
Currently attempting to save an array that is populated according to which cells in a UITableView are chosen and saving this array in an instance of a seperate object. I am getting the array to populate just fine, however, my save method, which is an IBAction that is invoked by clicking on a Bar Button doesn't seem to be working. Here is some code:
-(IBAction)saveWorkout:(id)sender {
Workouts *new = [[Workouts alloc] init];
[new addNewWorkout:customWorkout];
[customWorkout removeAllObjects];
}
This code is from the first class.
And here is the code for my addNewWorkouts method in the Workouts class:
-(void)addNewWorkout:(NSMutableArray*)array {
NSMutableArray *temp = [[NSMutableArray alloc] init];
temp = array;
self.workoutList = temp;
[temp release];
}
Here is my "Workout.h"
#import <Foundation/Foundation.h>
#interface Workouts : NSObject {
NSString *workoutName;
NSMutableArray *workoutList;
NSString *description;
int *reps;
int *weights;
int *sets;
}
#property (nonatomic, retain) NSString *workoutName;
#property (nonatomic, retain ) NSString *description;
#property (nonatomic, retain) NSMutableArray *workoutList;
-(void)addNewWorkout:(NSMutableArray*)array;
#end
Before running this code, I get a Warning from Xcode saying that 'Workouts may not respond to 'addNewWorkouts.'
Anyone know what is causing this error? Once I build & run, I click on the Save button and the app crashes with a unrecognized selector sent to instance 0x3b04410 error.
You call [new addNewWorkouts:customWorkout]
when the method's selector is addNewWorkout: (note that there is no plural in the method name)
This will make a bad method call and result in a crash.
Also, there is a problem with the memory management of the addNewWorkout method.
1- NSMutableArray *temp = [[NSMutableArray alloc] init];
2- temp = array;
3- self.workoutList = temp;
4- [temp release];
You allocate a new NSMutableArray on line 1, then lose its reference on line 2 when you replace its pointer by 'array'. The allocation you just made is lost and the program will leak.
Then, on line 4, you send a release message to 'temp' which actually points to 'array', resulting in the release of the parameter that you received and not the temporary object.
Is there a reason whny you create a temporary array? You can just assign the property and make the property copy or retain it, depending on your needs.
Iam getting an EXC_BAD_ACCESS all the time and I cannot figure out why...
Simple task:
The Parser Class pases XML with touchXML in an NSMutableArray called listArray.
In the Method grabCountry I can access the listArray and listArray.count works well.
Now I need the listArray.count in another Class the MasterViewController.
But Im getting an EXC_BAD_ACCESS error all the time.
Please help!
Here is the code snipplet:
Parser.h
#import <Foundation/Foundation.h>
#interface Parser : NSObject
#property (strong, retain) NSMutableArray *listArray;
#property (strong, retain) NSURL *url;
-(void) grabCountry:(NSString *)xmlPath;
#end
Parser.m
#import "Parser.h"
#import "TouchXML.h"
#implementation Parser
#synthesize listArray;
#synthesize url;
-(void) grabCountry:(NSString *)xmlPath {
// Initialize the List MutableArray that we declared in the header
listArray = [[NSMutableArray alloc] init];
// Convert the supplied URL string into a usable URL object
url = [NSURL URLWithString: xmlPath];
//XML stuff deleted
// Add the blogItem to the global blogEntries Array so that the view can access it.
[listArray addObject:[xmlItem copy]];
//works fine
NSLog(#"Amount: %i",listArray.count);
}
#end
MasterViewController.h
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import "TouchXML.h"
#import "Parser.h"
#class Parser;
#interface MasterViewController : UITableViewController{
Parser *theParser;
}
#end
MasterViewControlelr.m
- (void)viewDidLoad
{
NSString *xmlPath = #"http://url/to/xml.xml";
theParser = [[Parser alloc] init];
//Starts the parser
[theParser grabCountry:xmlPath];
//Here I want to access the Array count, but getting an BAD ACCESS error
NSLog(#"Amount %#",[theParser.listArray count]);
[super viewDidLoad];
}
Can anyone explain me what the problem here is?
Thanks!
Internally, each #property has a corresponding instance variable.
In your -grabCountry method, you are directly accessing the instance variable in the statement listArray = [[NSMutableArray alloc] init]; (same with url = [NSURL URLWithString: xmlPath];), instead of the #property's setter method, causing the NSMutableArray that you alloc-init'd to not be retained by the property. To invoke the #property's setter method, you should call
NSMutableArray *temp = [[NSMutableArray alloc] init];
self.listArray = temp; // or [self setListArray:temp];
[temp release];
If you want to have Xcode show an error when you are directly accessing the instance variable of an #property, you can have #synthesize listArray = _listArray, which changes the name of the instance variable to _listArray.
Generally, if there is an alloc-init, there must be a corresponding release (except if using Automatic Reference Counting).
Also, in the [listArray addObject:[xmlItem copy]]; statement, the call to copy is not needed, as NSArrays retain every object that is added to them. Calling copy also increases the retain count, which is another leak. Instead, you should just have [self.listArray addObject:xmlItem];
You are getting EXC_BAD_ACCESS because in NSLog(#"Amount %#",[theParser.listArray count]);, you are using %# format specifier, which is for NSStrings. You want to print the array's count, an integer, so you should be using %d or %i.
I have numerous classes that use the various NSDictionary/NSArray collection classes as ivars but often I run into the problem of my collection class getting released before the containing class is released.
This seems to happen mostly with the collections classes and not with another model class (ie classes that I either created separately or other NS* non-collection classes).
Here are the two variations I've done and seen other people do:
#implementation ClassX
// myDictionary declared as a property in the .h file as this:
// #property (nonatomic, retain) NSMutableDictionary *myDictionary;
#synthesize myDictionary;
- (id)int
{
if (self = [super init])
{
// Option 1:
// If I don't instantiate and assign with 'self',
// myDictionary ivar will not be available
// at times in doSomething.
myDictionary = [NSMutableDictionary dictionary];
// Option 2:
// Doing this, however will keep the dictionary around.
// because I have invoked an extra retain on the dictionary
self.myDictionary = [NSMutableDictionary dictionary];
// Which one is more correct?
}
return self;
}
- (void)doSomething
{
// this will give the error about trying to invoke
// a method on an already released instance
[myDictionary objectForKey:#"myKey"];
}
- (void)dealloc
{
// If I did self.myDictionary in 'init', I then
// need to do this:
[myDictionary release];
[super dealloc];
}
#end
So which approach is the more correct way to hold an instance of NSDictionary within a class?
Option 2 is correct; Option 1 is wrong.
But you left out the best option: myDictionary = [[NSMutableDictionary alloc] init].
I recommend using
myDictionary = [[NSMutableDictionary alloc] init];
The memory is only within the scope of the method you're in if you call [NSMutableDictionary dictionary]. Once you leave the method, that memory goes with it which is why you need to alloc/init if you want to retain the values.
That's why you don't have to release if you don't encounter an alloc.
So for instance:
- (void) doSomething {
// Do not need to release this string
NSString *someText = #"Hello world!";
// You need to release this string:
NSString *otherText = [[NSString alloc] initWithString:#"Hello world!"];
[otherText release];
}
Edited: Removed self after #mipadi #st3fan and caught my mistake. Forgot to post the change. Thanks for keeping me accountable.
An iPhone question for you guys! I have an NSURLConnection that downloads XML from a server and processes it into an array which is a part of another array. I have no way of knowing how many objects I will need, so I cannot allocate an NSArray beforehand. My question is:
Would it be better to create the parent array as an NSArray at the class level and allocate it after I store the data in a temporary NSMutableArray or just make the NSMutableArray at class level? It is worth noting that I do not need to modify the array other than to release it at the end of the program run.
I don't think it really matters.
I'm reading the Beginning iPhone 3 Development book at the moment, and usually loading the data is done like this:
You'd have an NSArray property :
#interface
{
...
NSArray *listOfObjects;
...
}
#property (nonatomic, retain) NSArray *listObObjects;
...
Then you create an NSMutableArray, load your data and set the property:
NSMutableArray *array = [[NSMutableArray alloc] init]; // ?
// load the XML into array here
...
self.listOfObjects = array;
[array release];
listOfObjects would then be treated as an NSArray (immutable) although it actually would be an NSMutableArray.
I think what you probably want to do is create some Classes that match what you are representing in your xml. For example if you xml looks something like this:
<peopleList>
<person>
<name>Joe</name>
<possession>Shovel</possession>
</person>
<person>
<name>Sam</name>
<possession>Shovel</possession>
<possession>Fork</possession>
<possession>Backpack</possession>
</person>
</peopleList>
You should have a PeopleList Class and a Person Class. An object instantiated from the PeopleList Class has your first array that contains one or more Person objects. The Person objects, in turn, also have arrays to hold the possesions (which in this case are strings - although if needed they could be Possesion objects) In this case, to help the example the Person Class also has another property: 'name' which is also a String.
For example:
#interface PeopleList {
NSMutableArray *persons; // An array to store the Person objects
}
#property (nonatomic, retain) NSMutableArray *persons;
#end
#interface Person {
NSString *name;
NSMutableArray *possesions; //An array to store this Person's possesion strings
}
#property (nonatomic, retain) NSString *name;
#property (nonatomic, retain) NSMutableArray *possesions;
#end
In the init method's of these objects you will have to alloc/init the arrays so they will be ready to receive objects. And because I alloc'd them, my class is responsible for the release.
#implementation PeopleList
-(id) init {
if (self = [super init]) {
persons = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[persons release];
[super dealloc];
}
#end
#implementation PeopleList
-(id) init {
if (self = [super init]) {
possesions = [[NSMutableArray alloc] init];
}
}
-(void) dealloc {
[possesions release];
[super dealloc];
}
#end
Now that this is done, you can set up your data structure of cascading array's.
As you are parsing the XML when you come across a PeopleList Tag do a:
currentPeopleList = [[PeopleList alloc] init];
and when you come across a Person tage do a:
currentPerson = [[Person alloc] init];
[peopleList.persons addObject: person];
a possesion:
[currentPerson.possesion addObject: contentsOfCurrentElement];
or name:
currentPerson.name = contentsOfCurrentElement;
But to answer your more specific question, I not store the data in a temporary NSArray and then copy that into a NSMutableArray. There is almost no performance gain by doing so, and you will burn cpu cycles and memory doing the copy.