ObjectiveC - Releasing objects added as parameters - iphone

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.

Related

reinitialize/reallocate an object with ARC

Im wondering if calling [[MyClass alloc] init] on an already existing (allocated) object create a leak in ARC? or does it manage the release and reallocation for you?
would doing the following be more proper:
myObject = nil;
myObject = [[MyClass alloc] init];
which is more appropriate? is there another way to do this properly?
With ARC, you don't need to set the myObject to be nil before reallocating it,only this line of code will be fine:
myObject = [[MyClass alloc] init];
When xcode compiles the code, it will automatically add the right release mechanism in.

do I need to allocate and init variables with retain properties?(iphone)

#interface Approval : NSObject
{
NSMutableArray *approvalValues;
}
#property (nonatomic,retain) NSMutableArray *approvalValues;
If i do this, do I still need to call `approvalValues = [[NSMutableArray alloc] init] in the init method? I was under the impression that I had to but it is causing a leak. In the dealloc method I am releasing approvalValues
You need to alloc and init approvalValues. The problem seems to be related to the fact that you are over-retaining your object.
Your code probably looks like this:
self.approvalValues = [[NSMutableArray alloc] init];
alloc will return an object with a retainCount of 1, and when using the retain setter it will get bumped to 2. In order to solve it, you might want to autorelease the object before assigning it, making a code that looks like this:
self.approvalValues = [[[NSMutableArray alloc] init] autorelease];
This will end up with an instance variable with a retainCount of only 1, so when you dealloc the object it won't leak.
Yes you still need to alloc/init, however you only release in dealloc method.
In the init method you will often access the ivar directly and initialize it like this:
approvalValues = [[NSMutableArray alloc] init];
In the dealloc you will need a matchin release like this:
[approvalValues release];
It is often recommended to access the ivars directly in the init and dealloc method to avoid any side effects caused by setters/getters.
Throughout your class you will want to use the KVC setters/getters or dot notation to set objects like this
// Dot notation
NSMutableArray *tmpApprovalValues = [[NSMutableArray alloc] init];
self.approvalValues = tmpApprovalValues;
[tmpApprovalValues release]; tmpApprovalValues = nil;
// Call to setters/getters
NSMutableArray *tmpApprovalValues = [[NSMutableArray alloc] init];
[self setApprovalValues:tmpApprovalValues];
[tmpApprovalValues release]; tmpApprovalValues = nil;
Corrected terminology thanks to #Yuji

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

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]];

What's the point of this in objective-c

SomeObject *temp = [[SomeObject alloc] init]
self.theObject = temp;
[temp release];
Why is it always done that way? Why not
self.theObject = [[SomeObject alloc] init];
If the theObject property is a retaining property, the first way is correct, because it doesn't leak memory. It's also more efficient than the correct way to write the second version, which is this:
self.theObject = [[[SomeObject alloc] init] autorelease];
Whenever you create an object with alloc you're in charge of releasing it somehow, whether by release or autorelease.
The second version leaks the SomeObject instance, since self.theObject will call a setter that, if properly written, retains the object.
You could just do
theObject = [[SomeObject alloc] init];
and some people certainly do. Others prefer to always use accessors though, either for consistence or to avoid bugs if the accessors have side effects (for exmaple, you would be bypassing KVO notification, which could be a problem if it's not part of an init method).