Does NSArray initWithObjects: retain objects? - iphone

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

Related

NSMutableArray: Memory management while calling methods

In my iOS app, I am using a NSMutableArray, named imageMArray. I have set its getter and setter properties and instantiated it.
In viewDidLoad:
imageMArray=[[NSMutableArray alloc] initWithArray:CategoryImages];
imageMArray=[self shuffleOnlyArray:imageMArray];
In ShuffleOnlyArray Method:
NSMutableArray *destArray1 = [[NSMutableArray alloc] initWithCapacity: [sourceArray count]] ;
return destArray1;
In shuffle Method:
imageMArray=[[self shuffleOnlyArray:imageMArray] retain];
There appears to be a memory leak in the Shuffle method.
Should I release imageMArray or set it to nil? If it should be released, should it be autoreleased?
imageMArray=[[NSMutableArray alloc] initWithArray:CategoryImages];
In the above statement, you have a memoryleak.
Instead you can have like as follows.
imageMArray = [NSMutableArray arrayWithArray:CategoryImages];
In ShuffleOnlyArray Method, return the autoreleased object.
NSMutableArray *destArray1 = [[NSMutableArray alloc] initWithCapacity: [sourceArray count]] ;
return [destArray1 autorelease];
But after you get it, retain (take the ownership) the array object.
imageMArray=[[self shuffleOnlyArray:imageMArray] retain];
Edit
In shuffle method, do as follows:
NSMutableArray *imageMArray1 = [imageMArray mutableCopy];
if( imageMArray )
{
[imageMArray release];
}
imageMArray=[[self shuffleOnlyArray:imageMArray1] retain];
[imageMArray1 release];
Edit 2:
One more solution:
Use the category to shuffle as mentioned in the SO link
No need of creating new and releasing the arrays.
1 You already have a memory leak in the following lines.
imageMArray = [[NSMutableArray alloc] initWithArray:CategoryImages];
imageMArray = [self shuffleOnlyArray:imageMArray];
In the first line you create an object with retain count 1.
Then you say that your imageMArray pointer points to other object. You should release the first object, because you louse the reference to the fist object and you can not release it after you change the reference!
2 You should not use retain because your ShuffleOnlyArray method returns a retained object.
Your factory method should return an autorelease object and the caller of the factory should decide if if will retain it or not.
Hope I was clear enough

iPhone ivars vs using self: don't need autorelease? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Use autorelease when setting a retain property using dot syntax?
What is difference between using ivars and self. notation?
instanceVar is instance variable declared with retain.
1) instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"]; //do I need autorelease here?????
2) self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"] autorelease];
Also, Do I need autorelease in the first situation?
This is explained in multiple places but seems as you asked what the different is
The first call is unchanged and looks like this:
instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"];
The second call when compiled will look something like this (assuming you have used a #property with retain and #synthesize:
self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"];
// The previous line will compile to this next line
[self setInstanceVar:[[NSMutableArray alloc] initWithObjects:#"1", #"2"]];
The body of the - (void)setInstanceVar:(NSMutableArray *)instanceVar; method will look something like this (the compiler create this for you because of your #property and #sythesize):
- (void)setInstanceVar:(NSMutableArray *)anInstanceVar
{
if (instanceVar != anInstanceVar) {
[instanceVar release];
instanceVar = [anInstanceVar retain];
}
}
Therefore in the call
self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"];
You have the +1 retain count on the newly created NSMutableArray and then you have the +1 retain count added from going through the setter.
This means that you require the extra release to match retains you are taking. It is considered better to not use autorelease in iPhone so you can be sure memory is being freed when you want it to. Therefore you should normally take the pattern
Create local var
Assign local var to ivar through setter
release local var
Which looks like this (FIXED thanks to #jamapag)
NSArray *tmpMyArray - [[NSArray alloc] initWithObject:#"Hello"];
self.myArray = tmpMyArray;
[tmpMyArray release]; tmpMyArray = nil;
1) instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"]; //do I need autorelease here?????
The NSmutableArray is created with a retain count of 1, you need to release your instanceVar in your dealloc() method
2) self.instanceVar = [[NSMutableArray alloc] initWithObjects:#"1", #"2"] autorelease];
Here you are using the setter, and since it is declared with retain it will increase its retain count by 1, the alloc init already increased the retain count by 1, so the total retain count is 2. However the autorelease msg will decrease this by 1 probaby in the next run loop. So again you only have to release this on your dealloc() method.
In the first situation you probably DO NOT want to autorelease, since this is an IVar you will probably want to use it again, and if you autorelease it the retain count will be 0 soon (most likely in the next run loop)

ObjectiveC - Releasing objects added as parameters

Ok, here goes.
Being a Java developer I'm still struggling with the memory management in ObjectiveC. I have all the basics covered, but once in a while I encounter a challenge.
What I want to do is something which in Java would look like this:
MyObject myObject = new MyObject(new MyParameterObject());
The constructor of MyObject class takes a parameter of type MyParameterObject which I initiate on-the-fly.
In ObjectiveC I tried to do this using following code:
MyObject *myObject = [[MyObject alloc] init:[[MyParameterObject alloc] init]];
However, running the Build and Analyze tool this gives me a "Potential leak of an object" warning for the MyParameter object which indeed occurs when I test it using Instruments. I do understand why this happens since I am taking ownership of the object with the alloc method and not relinquishing it, I just don't know the correct way of doing it.
I tried using
MyObject *myObject = [[MyObject alloc] init:[[[MyParameterObject alloc] init] autorelease]];
but then the Analyze tool told me that "Object sent -autorelease too many times".
I could solve the issue by modifying the init method of MyParameterObject to say return [self autorelease]; in stead of just return self;. Analyze still warnes about a potential leak, but it doesn't actually occur. However I believe that this approach violates the convention for managing memory in ObjectiveC and I really want to do it the right way.
Thanx in advance.
Ok, here's a suggestion.
MyParameter *param = [[MyParam alloc] init];
MyObject *obj = [[MyObject alloc] initWithParam:param]; // do you know if param is retain'd or not?
[param release];
Why do it this way? This is the pattern used throughout ObjC/Cocoa. You add objects to NSArrays this way:
MyThing *thing = [[MyThing alloc] init];
[myMutableArray addObject: thing]; // this calls [thing retain]
[thing release];
You may also want to try to do this:
MyObject *obj = [[MyObject alloc] initWithParam: [MyParameter parameter]];
where:
+ (id) parameter
{
return [[[self alloc] init] autorelease];
}
that way you don't have to worry about it. This is the same as
[NSData data];
[NSArray array];
I hope that helps. Generally, it isn't a good idea to use init during another method call (like a setter or another init). Hiding it behind a Class Method (+ (id) parameter) means the user knows it'll be autorelease'd.
If you're ever unclear about how many retain's or release's something has, you can always do something like this:
[EDIT]
Caveat: Apparently you should never use -retainCount. I find it useful for teaching learners the basics of retain'd Memory Management, but the point is well taken.
Never Use it in actual code or for performance testing. This is only used to learn what retain does (and I believe it functions properly in this case). It will never give you an intelligent answer for an object which is autorelease'd or further retain'd by a NSArray or Other Foundation/AppKit/UIKit Classes
MyParameter *param = [[MyParam alloc] init];
NSLog(#"param retain count: %d",[param retainCount]); // should be 1
MyObject *obj = [[MyObject alloc] initWithParam:param];
NSLog(#"param retain count: %d",[param retainCount]); // should be 2, if MyObject retains it.
[param release];
NSLog(#"param retain count: %d",[param retainCount]); // should be 1
Also, when you dealloc MyObject, you'll need to release param if you retain'd it during initialization.
The following guide put out by Apple should help you to understand Objective-C Memory Management a little better.
MyThing *thing = [[MyThing alloc] init];
[otherThing methodWithAThing:thing];
[thing release];
or:
[otherThing methodWithAThing:[[[MyThing alloc] init] autorelease]];
or (if there is a "convenience constructor" on the class you're using):
[otherThing methodWithAThing:[MyThing thing]];
MyObject *myObject = [[MyObject alloc] init:[[[MyParameterObject alloc] init] autorelease]];
should be ok, if there is no release in the init (there should definitely not be a release in the init). I often make a class method which makes a autoreleased object. So the code would be:
// using it:
MyObject *myObject = [[MyObject alloc] init:[MyParameterObject defaultParameters];
// in the class implementation of MyParameterObject
+ (MyParameterObject*) defaultParameters{
MyParameterObject* out = [[MyParameterObject alloc] init];
// set some values
return [out autorelease];
}
[[MyObject alloc] init:[[[MyParameterObject alloc] init] autorelease]]
Without knowing what's going on in the init method, this seems fine.
NB, though, that it's more Objective-Cish to spell this "initWithParameterObject:". Though they gag people new to the language, Obj-C's descriptive method names are actually really helpful for code readability.

Why Instruments report a leak?

I am developing an iphone app. Instruments reported a leaked object ServiceTypes. Below is the relevant code. Does anyone have any ideas? Thanks a lot for your help.
ServiceTypes *serviceTypes = [[ServiceTypes alloc] init];
if ([userConnection getServiceTypes:serviceTypes]) {
if ([serviceTypes.types length] > 0) {
NSArray *array = [[NSArray alloc] initWithArray:[serviceTypes.types componentsSeparatedByString: SERVICE_TYPE_DELIMITOR]];
serviceRequestTypes = [[NSMutableArray alloc] initWithArray:array];
[array release];
}
}
[[self typesTableView] reloadData];
[serviceTypes release];
It doesn't look like serviceTypes is being leaked. From the code you posted, serviceTypes is always released at the end of the method, and it doesn't appear to be retained anywhere in your sample. My question is: what happens inside getServiceTypes:. Does that method retain the serviceTypes parameter?
One more thing. If serviceRequestTypes is an instance variable (and it looks like it is), then you may be leaking memory by reassigning it without releasing the existing serviceRequestTypes object first. You should either rewrite serviceRequestTypes to be a property and use a synthesized accessor or make sure to release it every time before assigning. If its current value is nil, no big deal; the release message will simply be ignored. For example:
[serviceRequestTypes release];
serviceRequestTypes = [[NSMutableArray alloc] initWithArray:[serviceTypes.types componentsSeparatedByString:SERVICE_TYPE_DELIMITER]];

Creating Objects & Setting iVars in a Loop?

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.