Creating Objects & Setting iVars in a Loop? - iphone

NSArray *planetArray = [NSArray arrayWithObjects:#"Earth",
#"Jupiter",
#"Saturn",
#"Neptune",
#"Pluto", nil];
NSMutableArray *objectArray = [[NSMutableArray alloc] init];
for(NSString *eachPlanet in planetArray) {
Planet *newPlanet = [[Planet alloc] init];
[newPlanet setValue:eachPlanet forKey:#"name"];
[newPlanet setValue:#"TEST" forKey:#"type"];
[newPlanet setValue:[NSNumber numberWithInt:1234] forKey:#"mass"];
[objectArray addObject:newPlanet];
[newPlanet release];
}
for(Planet *displayEachPlanet in objectArray) {
NSLog(#"DATA: %#", displayEachPlanet);
}
[objectArray release];
I am curious if this is the best way to create an object and set an iVar for each item in an array. Basically I am:
Creating a Planet object
Setting the iVar (from the NSString array)
Adding the Planet object to an array.
Releasing the Planet object
Printing my Planet objects
Releasing the array
NB: I am just testing, this is not for anything, I was just curious ...
cheers Gary

Can't see anything drastically wrong about doing it that way. One suggestion would be to have an extended initialiser for your planet class, along the lines of:
-(Planet*) initWithName:(NSString*)name andType:(NSString*)type withMass:(int)mass;
And then create the planet with:
Planet *newPlanet = [[Planet alloc] initWithName:eachPlanet andType:#"Test" withMass:42];

Looks good to me. If all you are doing with the objects is printing something from them, you could probably do it in one loop with less initializing and such, but if thats just a test..it looks fine.

Related

Releasing Object is it necessary?

I am stuck in the middle of memory management stuff. Please help me out in solving my question.
NSMutableArray *array = [[NSMutableArray alloc]init];
Object *obj = [[Object alloc]init];
[array addObject: obj];
[obj release];
Is it necessary to release obj in above code?
The answer to your question is: yes, if you don't use ARC. If you are writing a new app, you should seriously consider using ARC.
Container objects in Objective-C always balance their retain/release count. In other words, you should always manage memory as if you did not add the object and make sure your own code balances its retain count. Note that this is a convention and is not enforced, but you could always trust the built-in classes to follow this convention. Also, you can perform a static analysis (Cmd+Shift+B in XCode) to detect these problems. It would have pinpointed this in your code above.
The correct code in the case above would be:
NSMutableArray *array = [[NSMutableArray alloc]init];
Object *obj = [[[Object alloc]init]autorelease];
[array addObject: obj];
or
NSMutableArray *array = [[NSMutableArray alloc]init];
Object *obj = [[Object alloc]init];
[array addObject: obj];
[obj release];
since NSMutableArray (and its cousins) will retain the object as long as it is in the collection.

memory leak situation in iphone

I have a memory leak when i call a method that return me a string----
the method definition is as follows
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
this show a big memory leak
i also tried--- NSMutableArray *dataArray = [[[NSMutableArray alloc] init]autorelease];
but this time leack checking process gets hanged
i also cannot release that array before return
please help
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return dataArray;
}
Anything that uses the method read will expect to get back an object it does not own. However, as written here, dataArray is still owned at the point of return. You can't release it because that might make it go away altogether. You must, in this instance autorelease the array. You can either do this:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[[NSMutableArray alloc] init] autorelease];
//picking data from database here
return dataArray;
}
or this
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [[NSMutableArray alloc] init];
//picking data from database here
return [dataArray autorelease];
}
You say "leak checking process get hanged" but I'm really not sure what you mean by that. Whether it hangs, crashes or plays the Botswana National Anthem, you definitely need to autorelease the returned array and any other problem is actually a different problem. Possibly, you are forgetting to retain the data elsewhere.
Another answer more...
There are many conventions in cocoa/cocoa-touch, there is one of them that says that if a method has the prefix init then you will have the ownership of that object (hence you have to release it)
This is NOT your case, hence if you do:
DatabaseReader *dbReader = [[DatabaseReader alloc] init];
NSMutableArray *mutArray = [dbReader read];
[dbReader release];
you are NOT supposed to release mutArray. BUT, the object created HAS to be released by someone. So you can do as JeremyP wrote. alloc/init and put it into a autorelease pool inside read method implementation. Or, you can do:
-(NSMutableArray *)read
{
NSMutableArray *dataArray = [NSMutableArray array];
//IMPORTANT:
//Did you noticed that I am not using any method
//with init prefix for the creation of dataArray ?
//so I don't need to release by my self ;)
//picking data from database here
return dataArray;
}
Which is basically the same. ;)
Ownership of the returned object may be returned to the object that receives from this function. You may do some debugging with the object's retain count using something like this...
NSLog(#"Retain count: %i", [dataArray retainCount]);
Turn on the debugging console (Command + R in Xcode) to see the NSLog output.

Objective C two-dimensional array memory issues

I'm trying to declare a two-dimensional array as an instance variable in Objective C. I've got the NSMutableArray in the header (data), along with the #property (nonatomic, retain). In viewDidLoad: I have:
data = [[NSMutableArray alloc] init];
[data addObject:[[NSArray alloc] initWithObjects:#"Cheese", #"Meat", #"Veggie", nil]];
[data addObject:[[NSArray alloc] initWithObjects:#"Sandwich", #"Soup", #"Stew", nil]];
I can NSLog the array within the method and it is correct, however when I try to Log it from a separate method I get nothing (just "#"), and if I try to access with
NSInteger num = [[data objectAtIndex:component] count];
it crashes with no error in the log. I'm sure this is something to do with not allocating memory properly, however I am new to Obj C and haven't worked with a C-style language in many years. FWIW, I have tried many variants of this that all fail, including using NSArray instead of mutable, [NSArray arrayWithObjects] instead of [[NSArray alloc] initWithObjects], and every combination in between.
try creating the outer array like this:
self.data = [NSMutableArray arrayWithCapacity:2]; // assuming you're only adding 2 inner arrays.
The following may be a right way.
self.data = [NSMutableArray array];
[data addObject:[NSArray arrayWithObjects:#"Cheese", #"Meat", #"Veggie", nil];
[data addObject:[NSArray arrayWithObjects:#"Sandwich", #"Soup", #"Stew", nil];
Note that, as #jamihash commented above, you need self.data to properly retain the array. And, there is no need to alloc the NSArray which you are adding to data.
As a side issue, you're retaining the child arrays twice. They get retained when you add them to your NSMutableArray, so you should probably autorelease them on creation or create them with one of the NSArray methods that returns an autoreleased array.
Your code by itself shouldn't crash. You should look into where and when you release and retain the NSMutableArray. You could post more of the code and I'm sure somebody will spot the problem.

Does NSArray initWithObjects: retain objects?

When adding objects to an NSArray using "initWithObjects" can anyone confirm for me that the items are retained. I am pretty sure they are, but can't find it mentioned anywhere with regards to initWithObjects?
// CREATE DRINKS
Coffee *drink1 = [[Coffee alloc] initWithName:#"Flat White"];
Coffee *drink2 = [[Coffee alloc] initWithName:#"Cappucino"];
Coffee *drink3 = [[Coffee alloc] initWithName:#"Latte"];
Coffee *drink4 = [[Coffee alloc] initWithName:#"Mocha"];
Coffee *drink5 = [[Coffee alloc] initWithName:#"Hot Chocolate"];
// SET ARRAY
NSArray *tempArray = [[NSArray alloc] initWithObjects:drink_1, drink_2, drink_3, drink_4, drink_5, nil];
[self setCoffeeList:tempArray];
// CLEAN UP
[drink_1 release];
[drink_2 release];
[drink_3 release];
[drink_4 release];
[drink_5 release];
[tempArray release];
[super viewDidLoad];
cheers Gary
initWithObjects retains all items in the array.
initWithObjects: count:
(id) initWithObjects: (id*)objects count: (NSUInteger)count;
Availability: OpenStep
This is a designated initialiser for the class.
Subclasses must override this method.
This should initialize the array with count (may be zero) objects.
Retains each object placed in the array.
Calls -init (which does nothing but maintain MacOS-X compatibility), and needs to be re-implemented in subclasses in order to have all other initialisers work.
Objects are expected to take ownership of the things they need to keep around. An array is responsible for its items, therefore it retains them. See the memory management guide for complete details. (No, seriously, read it. You'll thank yourself later when you don't have to ask this question about every class you use and your program isn't crashing every five seconds.)

NSDictionary functionality question

So I am trying to save arrays into an NSDictionary on the fly. Let me show you the code and explain what is going on.
for (int x= 0; x <[appDelegate.people count]; x++) {
Person *aPerson = [[Person alloc] init];
aPerson = [appDelegate.people objectAtIndex:x];
if ([appDelegate.groupedBusiness objectForKey:aPerson.business_name] == nil) {
NSMutableArray *newBusiness = [[NSMutableArray alloc] init];
//if the business does not exist in the dict, add the person to the business and add it to dict.
[newBusiness addObject:aPerson];
[appDelegate.groupedBusiness setObject:newBusiness forKey:aPerson.business_name];
[newBusiness release];
newBusiness = nil;
//NSLog(#"%#", appDelegate.groupedBusiness);
} else {
NSMutableArray *existingBusiness= [appDelegate.groupedBusiness objectForKey:aPerson.business_name];
[existingBusiness addObject:aPerson];
//THIS IS THE LINE I AM NOT SURE ABOUT!!!
[appDelegate.groupedBusiness setObject:existingBusiness forKey:aPerson.business_name];
[existingBusiness release];
existingBusiness = nil;
//NSLog(#"%#", appDelegate.groupedBusiness);
}
}
Alright, so the appDelegate has an array of "People" that has a whole bunch of attributes about a person. I am trying to set up a dictionary to sort them by their business names. I am doing this by creating an array and saving it in the dictionary with the business_name as the key. Each iteration of the loop I check to see if the key exists, if it does, pull out the existing array, add the person you are checking, and resave it to the dictionary. However, this does not appear to be happening. Is there some exotic behavior in the NSDictionary class that would prevent that? I have poured over the class web page and can't find anything. Sorry if this is a noobie question, I am still trying to understand the objective-c classes. Thanks!
Why do you release existingBusiness? You are not creating an object, just taking the pointer from an array. When you invoke release, retainCount became 0 and object deallocs.
Just remove the following two lines:
[existingBusiness release];
existingBusiness = nil;
and everything should work fine.
You're way overcomplicating this, not to mention leaking a couple things.
for (Person *aPerson in appDelegate.people) {
NSMutableArray *business = [appDelegate.groupedBusiness objectForKey:aPerson.business_name];
if (!business) {
business = [NSMutableArray array];
[appDelegate.groupedBusiness setObject:business forKey:aPerson.business_name];
}
[business addObject:aPerson];
}
Not an answer, but some coding style issues.
Use fast iteration if you don't need the index:
for (Person *aPerson in appDelegate.people) {
Use convenience constructors; it makes your code more readable (remember to remove the "release" at the end):
NSMutableArray *newBusiness = [NSMutableArray arrayWithObject:aPerson];
Avoid duplicate logic where possible:
NSMutableArray * business = [appDelegate.groupedBusiness objectForKey:aPerson.business_name;
if (!business) {
business = [NSMutableArray array];
}
[business addObject:aPerson];
[appDelegate.groupedBusiness setObject:business forKey:aPerson.business_name];
The "setObject:existingBusiness" call does changes nothing apart from wasting CPU cycles, but in the case above, it makes the code somewhat more readable.
[appDelegate.groupedBusiness setObject:existingBusiness forKey:aPerson.business_name];
[existingBusiness release];
existingBusiness = nil;
This should all be removed. existingBusiness is already in the dict, and it's a mutable object - when you're adding a person to it, this will be reflected in the dictionary as well as it's the same object you're dealing with. Apart from that you have a couple of memory leaks as Daniel points out.