I have a static method in a Utilities class:
+ (Division *) getDefaultDivision
{
Division *defaultDivision = [[Division alloc] init];
defaultDivision.Id = 0;
defaultDivision.name = #"Accounting";
defaultDivision.slug = #"accounting";
return defaultDivision;
}
And in my ViewController I do something like:
Division *div = [[Division alloc] init];
div = [Utilities getDefaultDivision];
But when I analyze, it says "Potential leak of an object allocated on line x and stored into defaultDivision".
If I use:
Division *defaultDivision = [[[Division alloc] init] autorelease];
it works once, but when I use it again, it crashes.
Just wondering what the proper thing is to do here?
If this is your real code;
Division *div = [[Division alloc] init];
div = [Utilities getDefaultDivision];
You're first allocating a Division and saving it in div, then you're getting a new one from getDefaultDivision storing that in div too without releasing the first one.
Regardless of how "getDefaultDivision" is implemented, you have a leak in this code:
Division *div = [[Division alloc] init];
div = [Utilities getDefaultDivision];
Line 1 allocates memory and assigns div to point to that memory. You must free this memory at some point. But after line 2, that becomes impossible, because div is now has a new value -- and the pointer to the memory allocated in line 1 is lost. This is a leak. Until you understand why, you are on thin ice.
As for this method:
+ (Division *) getDefaultDivision
{
Division *defaultDivision = [[Division alloc] init];
defaultDivision.Id = 0;
defaultDivision.name = #"Accounting";
defaultDivision.slug = #"accounting";
return defaultDivision;
}
This is sometimes called a "factory" method -- a static utility method for allocating, initializing, and returning reference to a new instance of a class. The best practice here is to use autorelease in factory methods. E.g.:
Division *defaultDivision = [[[Division alloc] init] autorelease];
Why is it a best practice? According to Apple's memory management guide, only methods with the following terms in them should return references to objects that the caller is responsible for releasing:
alloc, allocWithZone:, copy, copyWithZone:, mutableCopy, mutableCopyWithZone:
-- from http://developer.apple.com/library/IOs/#documentation/General/Conceptual/DevPedia-CocoaCore/MemoryManagement.html
Since "getDefaultDivision" is not an "alloc" or "copy" method (it is an accessor method), then it should not return a pointer to an object which the caller must later free to avoid a leak. Marking the newly allocated returned memory as autorelease is one way to follow this contract.
Related
Here is code I am referring to.
// Person.h
#interface Person : NSObject {
NSString *firstName;
NSString *lastName;
}
#end
// Person.m
#implementation Person
- (id)init {
if (![super init]) return nil;
firstName = #"John";
lastName = #"Doe";
}
#end
// MyClass.m
#implementation MyClass
.....
- (NSArray *)getPeople {
NSMutableArray *array = [[NSMutableArray alloc] init];
int i;
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
}
return array;
}
.....
#end
Now, I know there is no memory-management going on in this sample code. What would be required?
In the getPeople loop, I am alloc'ing a Person (retainCount 1), then adding it to array. The retain count is now 2, right? If it is two, should I be [p release]'ing after adding it to the array, bringing the retainCount back down to 1?
Am I right in that it is the caller's responsibility to release the array returned by the method? (Which would also free the memory of the Person's, and their instance variables, assuming their counts are at 1).
I have read Apple's memory management document, but I guess what I am most unclear about, is what increases an objects retain count? I think I grasp the idea of who's responsibility it is to release, though. This is the fundamental rule, according to Apple:
You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.
bobDevil's sentence "only worry about the retain counts you add to the item explicitly" made it click for me. After reading the Ownership policy at Apple, essentially, the object/method that created the new object, is the one responsible for releasing /it's/ interest in it. Is this correct?
Now, let's say I a method, that receives an object, and assigns it to a instance variable. I need to retain the received object correct, as I still have an interest in it?
If any of this is incorrect, let me know.
You are correct that the retain count is 2 after adding it to an array. However, you should only worry about the retain counts you add to the item explicitly.
Retaining an object is a contract that says "I'm not done with you, don't go away." A basic rule of thumb (there are exceptions, but they are usually documented) is that you own the object when you alloc an object, or create a copy. This means you're given the object with a retain count of 1(not autoreleased). In those two cases, you should release it when you are done. Additionally, if you ever explicitly retain an object, you must release it.
So, to be specific to your example, when you create the Person, you have one retain count on it. You add it to an array (which does whatever with it, you don't care) and then you're done with the Person, so you release it:
Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it
Also, as I said above, you only own the object during alloc or copy generally, so to be consistent with that on the other side of things, you should return the array autoreleased, so that the caller of the getPeople method does not own it.
return [array autorelease];
Edit:
Correct, if you create it, you must release it. If you invest interest in it (through retain) you must release it.
Retain counts are increased when you call alloc specifically, so you'll need to release that explicitly.
factory methods usually give you an autoreleased object (such as [NSMutableArray array] -- you would have to specifically retain this to keep it around for any length of time.).
As far as NSArray and NSMutableArray addObject:, someone else will have to comment. I believe that you treat a classes as black boxes in terms of how they handle their own memory management as a design pattern, so you would never explicitly release something that you have passed into NSArray. When it gets destroyed, its supposed to handle decrementing the retain count itself.
You can also get a somewhat implicit retain if you declare your ivars as properties like #property (retain) suchAndSuchIvar, and use #synthesize in your implementation. Synthesize basically creates setters and getters for you, and if you call out (retain) specifically, the setter is going to retain the object passed in to it. Its not always immediately obvious, because the setters can be structured like this:
Person fart = [[Person alloc] init];
fart.firstName = #"Josh"; // this is actually a setter, not accessing the ivar
// equivalent to [fart setFirstName: #"Josh"], such that
// retainCount++
Edit:
And as far as the memory management, as soon as you add the object to the array, you're done with it... so:
for (i = 0; i < 10; i++) {
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
}
Josh
You should generally /not/ be worried about the retain count. That's internally implemented. You should only care about whether you want to "own" an object by retaining it. In the code above, the array should own the object, not you (outside of the loop you don't even have reference to it except through the array). Because you own [[Person alloc] init], you then have to release it.
Thus
Person *p = [[Person alloc] init];
[array addObject:p];
[p release];
Also, the caller of "getPeople" should not own the array. This is the convention. You should autorelease it first.
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];
You'll want to read Apple's documentation on memory management: http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
According to the OpenGL ES Programming guide's section on texturetool,
Your app must parse the data header to obtain the actual texture data. See the
PVRTextureLoader sample for an example of working with texture data in the PVR format.
This is all well and good, but 3 years later, and PVRTextureLoader's PVRTexture.m doesn't compile because it needs to be converted in order to work in an ARC project. I reckon I could flag these two files as non-ARC but I wanted to at least learn a little about Objective-C this time around.
Here's a bit of code that's giving me trouble at this point:
+ (id)pvrTextureWithContentsOfFile:(NSString *)path
{
return [self initWithContentsOfFile:path];
}
This was manually converted from:
+ (id)pvrTextureWithContentsOfFile:(NSString *)path
{
return [[[self alloc] initWithContentsOfFile:path] autorelease];
}
Maybe someone could be so kind as to walk through what this actually does (as it makes no sense to me to refer to self from what is clearly declared as a class method and not an instance method), but the actual error seen is
<...>/PVRTexture.m:256:15: error: no known class method for selector 'initWithContentsOfFile:'
return [self initWithContentsOfFile:path];
^~~~~~~~~~~~~~~~~~~~~~
1 error generated.
As for why I am manually converting this file to ARC, the Edit->Refactor->Convert to Objective-C ARC... menu option basically says "fix these 8 errors before I can continue" and these 8 errors are of course ARC-related errors. Which I was hoping the conversion would be able to resolve. Circular dependencies are only fun the first time around.
The curious bit is that -initWithContentsOfFile:path is right there in the file too:
- (id)initWithContentsOfFile:(NSString *)path
{
if (self = [super init])
{
NSData *data = [NSData dataWithContentsOfFile:path];
_imageData = [[NSMutableArray alloc] initWithCapacity:10];
_name = 0;
_width = _height = 0;
_internalFormat = GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
_hasAlpha = FALSE;
if (!data || ![self unpackPVRData:data] || ![self createGLTexture])
{
self = nil;
}
}
return self;
}
Note: This code compiles if I change the + to a - on the pvrTextureWithContentsOfFile declaration. I am positive the original code had a + there, so please, somebody help explain this to me.
alloc is a class method. You usually see alloc used from another class, with things like:
NSView *myView = [[NSView alloc] initWithFrame: someRect];
In a class method, self is the class, not an instance of that class. So [self alloc] allocates an instance of the class. It would also be valid to use the name of the class explicitly, so in the case of your PVRTexture class,
return [[self alloc] initWithContentsOfFile:path];
Could be replaced with
return [[PVRTexture alloc] initWithContentsOfFile:path];
Both are perfectly valid. I would probably use the second form, just because, like you, I find the first form a little odd-looking.
I got it, I think. Here's another example where writing out the question leads to the answer.
It's this:
return [[self alloc] initWithContentsOfFile:path];
I'm sure Google will be happy enough to (let SO) show me what alloc actually does. I guess calling [self alloc] from a class method is essentially what makes that method into a factory method that can generate instances of that class. This is nothing other than the Obj-C way to new something.
Me, I made the mistake of assuming alloc was something that ARC abolished.
Also, a side note: Reading carefully helps. I also just found out about GLKTextureLoader so I didn't need to convert PVRTextureLoader at all.
I have one property say
#property(nonatomic,assign) NSString *str;
Now I am having something like
self.str = [[NSString alloc] init];
self.str = #"test";
NSLog(#"%#",str);
[self.str release];
When I run I can see a leak "Potential leak of memory".
Why its showing me leak ?
Please guide me I am leaning phase of iOS
In other words (I'm just expanding on Anoop's answer here), you have two strings, not one.
self.str = [[NSString alloc] init];
self.str = #"test";
The thing on the right side of the first line is a string: [[NSString alloc] init]. But in the second line you throw it away, replacing it with a different string, namely #"test". Now there is NO REFERENCE pointing to the first string. Thus it leaks, since it can never be released nor can anything else ever be done to or for it.
The situation of the string created in the first line is, after the second line, like the situation of "thing1" in the second panel of this diagram:
No one is pointing to it, so its memory cannot be managed, and it lives forever in isolation (leak).
self.str = [[NSString alloc] init]; //1st
One string is allocated not used.
self.str = #"test"; // 2nd
Another constant string "test" is allocated but released.
So first one is a leak.
When you use factory method or create object using alloc,new,retain,copy,mutableCopy your object has +1 retain count every time. You own object in this case. You are responsible for releasing it. So you need to release object after you finish using object which cause -1 retain count to object.
It is not necessary to alloc you string again as you are already creating its property.
And it has its getter setter + you are allocating it again so gives you a leak.
I am using "LIExposeController" to create new UIViewControllers. This brings up some memory warnings that I am not able to fix.
- (UIViewController *)newViewControllerForExposeController:(LIExposeController *)exposeController {
UIViewController *viewcontroller = [[[MyViewController alloc] init] autorelease];
return viewcontroller; // warning here saying object with a + 0 retain count returned to caller where a + 1 (owning) retain count is expected
}
- (void)shouldAddViewControllerForExposeController:(LIExposeController *)exposeController {
[exposeController addNewViewController:[self newViewControllerForExposeController:exposeController]
animated:YES];
} // warning here saying potential leak of an object
LIExposeController *exposeController = [[[LIExposeController alloc] init] autorelease];
exposeController.viewControllers = [NSMutableArray arrayWithObjects:
[self newViewControllerForExposeController:exposeController],
nil]; // warning here saying potential leak of an object
A method starting with new is expected to produce an object with retain count +1 and not an autoreleased/retain count +0 object. ARC and the Static Analyzer both follow these conventions as rules, and you should fix your code to meet them or rename your method to not clash with the conventions.
I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}
Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.