0

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.

pajevic
  • 4,607
  • 4
  • 39
  • 73

4 Answers4

3

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.

Community
  • 1
  • 1
Stephen Furlani
  • 6,794
  • 4
  • 31
  • 60
  • I will probably do it using the first approach you suggested. I had thought about it myself, but your (entire) answer convinced me to. I guess I just need to accept that some of the "lazy" ways of doing stuff that I am used to from Java and C# don't always work in ObjectiveC :) – pajevic Jan 12 '11 at 20:35
  • 1
    +1 correct, **EXCEPT THAT YOU SHOULD NEVER USE `-retainCount`, FOR ANY REASON** – Dave DeLong Jan 12 '11 at 20:37
  • @NobleK, unfortunately not. Objective-C is very, very wordy. And they like very much to have single Method calls on individual lines. Also, by using the same patterns of code that Apple does, you'll confuse your Analyze tool less. – Stephen Furlani Jan 12 '11 at 20:38
  • 2
    Nope, not even for debugging. There are always better ways. http://stackoverflow.com/questions/4636146/when-to-use-retaincount – bbum Jan 12 '11 at 20:39
  • Just one note regarding Stephen Furlani's response: You should never use -retainCount, because it never tells you anything useful. – David Schaefgen Jan 12 '11 at 20:45
  • I've edited my post to reflect the **hate** thrown `-retainCount`'s way. :) Complete with links. – Stephen Furlani Jan 12 '11 at 20:46
  • [[[[self class] alloc] init] autorelease] can be shortened to [[[self alloc] init] autorelease]; self is already the class. – rpetrich Jan 13 '11 at 01:44
  • @rpetrich, I think that `[self class]` is more like a virtual call, and will return the subclass. So if he makes a `MySubParameter` the `+parameter` for `MyParameter` will make a `MySubParameter` not a `MyParameter` if he does `[MySubParameter parameter]` instead of `[MySubParameter subparameter]`. – Stephen Furlani Jan 13 '11 at 15:01
  • @rpetrich, ok so I've found conflicting results for `[self class]` whether it's necessary or not. [this](http://theocacao.com/document.page/473/) says one thing, and [this](http://www.cocoabuilder.com/archive/cocoa/56923-self-vs-self-class-in-class-methods-is-there-difference.html) says another. – Stephen Furlani Jan 13 '11 at 15:13
  • @Stephen Furlani: If you read all of your second link, the op is corrected. Class methods are dispatched identically to instance methods--`self` is the class object that received the message not the class object that handled it. – rpetrich Jan 15 '11 at 01:38
2
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]];
codelark
  • 12,254
  • 1
  • 45
  • 49
  • Yeah, autorelease should work, just don't understand why I get the warning in Analyze. I want to avoid making a variable for the parameter since I actually have many of these calls. The convenience constructor is not possible either (I think) since the actual init for the parameter object has parameters (int) itself (I was keeping it simple in the example). – pajevic Jan 12 '11 at 20:26
0
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];
}
Jaxan
  • 976
  • 7
  • 9
0
[[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.

Dan Ray
  • 21,623
  • 6
  • 63
  • 87
  • The init method looks fine (there is no autorelease there or something). I dont understand why Analyze gives the warning. And I do know about the naming convention. Had a hard time with it at first but I do appreciate them now :) Just tried to make the example above resemble the Java example as much as possible. – pajevic Jan 12 '11 at 20:20
  • @NobleK, unfortunately memory management between Java and Obj-C is so different that making them "resemble" will lead you down a dark path of frustration! `alloc/init` is not at all like `new`. Try instead patterning your code off of iPhone examples - [Ray Wenderlich](http://www.raywenderlich.com/) has a bunch of good ones. – Stephen Furlani Jan 12 '11 at 20:36
  • @NobleK - Clang does good work, but it does get some false positives. For instance, it you instantiate something that's going to do asynchronous work and call a delegate, and then you catch it and release it in the delegate call, well Clang can't follow that, and calls the object a potential leak at the end of the block where it's instantiated... – Dan Ray Jan 12 '11 at 20:37