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.
Related
I have some confusion using NSMapTable.
I have NSMapTable with NSPointerFunctionsWeakMemory:
_controllersTable = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality
valueOptions:NSPointerFunctionsWeakMemory|NSPointerFunctionsObjectPointerPersonality
capacity:0];
Assume I create UIViewController and add it to MapTable:
MyVC *myVC = [[MyVC alloc] init];
[self.controllersTable setObject:#1 forKey:controller]
MyVC.m
- (void)dealloc {
// [[[controllersTable keyEnumerator] allObjects] count]] == 0
}
So, by the time MyVC calls dealloc, there is not entry for that VC. Are there any way to get value from NSMapTable in that case?
PS
I can't use NSPointerFunctionsStrongMemory for storing keys, because it can introduce retain cycle.
The leak instruments warn me about a memory leak related to this part of code:
[self.contview addSubview:nav.view];
Here are how I manage the view:
[nav.view removeFromSuperview];
self.nav = [[[destinationClass alloc] initWithNibName:pagename bundle:nil] autorelease];
[self.contview addSubview:nav.view];
Is it normal that the self.nav has a retainCount of 2 just after been allocated?Could this be related to the memory leak?
I'm very new to the memory management can someone give me some help?
Many Thanks
Assuming nav is a strong (retain) property, it retains the view controller you are assigning here:
self.nav = [[[destinationClass alloc] initWithNibName:pagename bundle:nil] autorelease];
effectively, the retain count after this line of code is 1; +2 for alloc and retain and -1 for autorelease. Generally you should never use retainCount method to determine the actual retain count of the object, maybe this answer will give you more insight why.
Every alloc, retain or copy call should be matched with a release or autorelease call. You should add a matching release call in dealloc method of your class
-(void) dealloc {
[_nav release];
_nav = nil;
[super dealloc];
}
Don't use manual memory management, use ARC, it will make your life much easier :)
Assuming that a view controller is created like this:
#property (nonatomic, strong) SomeViewController *someViewController;
...
self.someViewController = [[SomeViewController alloc] initWithView:imgView];
[self addChildViewController:self.someViewController];
self.someViewController.view.frame = self.view.bounds;
[self.mainView addSubview:self.someViewController.view];
Why would it not get released by the following?
__weak MainViewController *weakSelf = self;
self.someViewController.didCloseBlock = ^{
[weakSelf.someViewController.view removeFromSuperview];
[weakSelf.someViewController willMoveToParentViewController:nil];
[weakSelf.someViewController removeFromParentViewController];
weakSelf.someViewController = nil;
};
I can tell it's not getting released because if I keep opening and closing the view controller (creating a new instance each time I open one), it causes low memory warnings (and then a crash on iOS5), and in SomeViewController didReceiveMemoryWarning, I see a log for the number of times I've created a new SomeViewController. For example, when I get the memory warning after opening 9 new SomeViewControllers, I will get 9 didReceiveMemoryWarning logs, indicating that I have 9 SomeViewController instances in memory, even though I'm nilling each one out in the code above.
You're retaining your view once in your property with the strong annotation and again with self.someViewController = [[SomeViewController alloc] initWithView:imgView];
Using the synthesized variable should get rid of this:
_someViewController = [[SomeViewController alloc] initWithView:imgView];
If you're not using ARC, you can use self.someViewController = [[[SomeViewController alloc] initWithView:imgView] autorelease];
I'd probably go for the first option, ARC or not though.
You are just setting the block didCloseBlock, nothing else actually. Do you execute it?
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.
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.