- (void)viewDidAppear:(BOOL)animated
{
[super viewWillAppear:animated];
_datePicker = [[UIDatePicker alloc] init];
_datePicker.datePickerMode = UIDatePickerModeDate;
_datePicker.frame = CGRectMake(70, self.view.frame.size.height ,250,100);
_datePicker.alpha = 0;
[_datePicker addTarget:self action:#selector(saveDate:) forControlEvents:UIControlEventEditingDidEnd];
[self.view addSubview:_datePicker];
}
I'm trying to figure out where is my "unbounded memory growth" , I'm using ARC in my app. I have a few memory issues in my app and I'm trying to figure them out one by one.
While moving between 2 viewControllers I can clearly see a big increase in memory use, the main cause for it is the code I wrote here. What am I doing wrong, how can I release it and where?
Thanks
viewDidAppear: gets called every time the view appears (even if the controller has been initialized already). So each time the view appears you are allocating a new UIDatePicker without releasing the old one.
If your datePicker is defined as a #property with "retain" then I would use
self.datePicker = [[[UIDatePicker alloc] init] autorelease];
By using self.datePicker you are calling the synthesized setter which will automatically release the old value for you.
Alternatively you can move this initialization to initWithNibName: or viewDidLoad: instead, that way it will only get called once.
Try this out and see if it helps your memory.
(I'm assuming you're not using ARC, otherwise what I said won't really help you).
Related
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 have an EAGLView-based class that runs the following code when a menu selection is made in OpenGL:
-(void) startPicker
{
self.gameState = kStatePicker;
GKPeerPickerController *picker = [[GKPeerPickerController alloc] init];
picker.delegate = self;
picker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
[picker show];
}
For some unknown reason, the picker is a blank rounded rect when it appears, with no visible interface or directions.
If I launch it at the end of my init function, it works. Afterward, it launches fine from the menu.
I have tried placing the code in startPicker inside a main queue dispatch, but that doesn't seem to help. I've tried running the picker with and without ARC, but that makes no difference. This code is more or less directly taken from the GKTank example that Apple provided a while ago, to introduce GameKit's bluetooth framework.
Can anyone tell me why this might be happening, and what a possible solution is?
Just following up. I discovered in the view controller that contains this view that there was a call to [UIView setAnimations:NO]; This prohibits GKPeerPickerController from appearing properly.
I have a question. There may be a very simple solution to this question but I am not able to figure it out yet. If I use a property say #property(nonatomic, retain)UIView *mainView.
Now I synthesize it in .m file and release it in the dealloc method as following:
- (void)dealloc {
[mainView release], mainView = nil;
[super dealloc];
}
Then in my viewDidLoad, I'm allocating it and adding it as the subview of my self.view like following:
- (void) viewDidLoad {
mainView = [[UIView alloc] init];
.
.
.
[self.view addSubView: mainView];
}
Now I understand that at this point my mainView would have 3 reference counts (one from alloc, one because it's a retained property, and the third one when I added it to self.view), its parent controller would own it too.
Now, my question is if after adding my view to self.view, I release my mainView using
[mainView release];
My app crashes when I go back to the previous view as I am sending release to already deallocated object. Now my question is how am I overreleasing my view here. what am I missing because when I use following code it works fine and no crashes occur.
- (void) viewDidLoad {
UIView *newView = [[UIView alloc] init];
self.mainView = newView;
[newView release];
.
.
.
[self.view addSubView: mainView];
}
I know why this second viewDidLoad method works but what I dont know is why first one fails, I am supposed to release my view after adding it to self.view. Right?
NOTE: I understand that in the first viewDidLoad, I can use autorelease method to release the view that is being assigned to the ivar and it wont crash but the whole point is I am trying to reduce the use of autorelease as much as possible. And I am not using ARC at all
I would really appreciate the explanation and suggestions.
From your question:
Now i understand that at this point my mainView would have 3 reference
counts (one from alloc, one coz its a retained property and the third
one when i added it to self.view)
You didn't assign through the property but assigned directly to the instance variable, so there was no retain; only in ARC does assigning to the instance variable retain the value. So, do not perform the manual release.
In order for your #property to retain the mainView, you should use it as self.mainView and not just mainView. If you use the latter alone, it will not retain it. Basically if you call self.mainView = ... it is calling a setter method for mainView which does a [mainView retain]; internally. When you are directly assigning it, it wont execute this setter and retain will not be executed.
You should try it like this,
self.mainView = [[[UIView alloc] init] autorelease];
[self.view addSubView:self.mainView];
or as shown in your question.
UIView *newView = [UIView alloc] init];
self.mainView = newView;
[newView release];
[self.view addSubView:self.mainView];
You can also try using ARC for your project. Your code will look like this in ARC,
self.mainView = [[UIView alloc] init];
[self.view addSubView:self.mainView];
Check the documentation for more details.
In your first viewDidLoad method you are not referring to self.mainView only mainView thats why its not retained, in order to retain property work you have to set mainView using self.mainView!
I am new to iPhone programming and Objective-C.
I am building a View-based application.
The problem is that none of the UIViewController's dealloc functions are ever called.
I have decided to unload all my retained objects programmaticaly, right before presenting the next UIViewController class.
I have resolved all the leaks detected by Xcode, tested the application with Leaks and Allocations Tools and everything seams OK, but the memory used builds up to around 180 MB and the app crashes.
The self.retainCount before presenting the next UIViewController is 1, the objects owned are released and the pointers are nil, but still the memory builds up.
Can you give me a clue? If needed I will post some code samples. Or can you send me some references to something like Objective-C memory management 101?
This is my interface:
#interface GameScreen : UIViewController
{
IBOutlet UILabel *timeLabel;
IBOutlet UIView *gameView;
IBOutlet UIImageView *gameViewFrame;
IBOutlet UIButton *showOutlineButton;
UIImage *puzzleImage;
UIView *optionsView;
UIView *blockView;
NSTimer *timer;
UIImageView *viewOriginalPicture;
SHKActivityIndicator *activityIndicator;
BOOL originalPictureShown;
BOOL outLineShown;
}
#property (nonatomic, retain) IBOutlet UILabel *timeLabel;
#property (nonatomic, retain) IBOutlet UIView *gameView;
#property (nonatomic, retain) IBOutlet UIImageView *gameViewFrame;
#property (nonatomic, retain) IBOutlet UIButton *showOutlineButton;
#property (nonatomic, retain) UIImage *puzzleImage;
And here is the implementation:
- (id) initWithPuzzleImage: (UIImage *) img
{
if((self = [super init]))
{
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
}
return self;
}
This is the function called when the user taps the exit button:
- (void) onExit
{
[timer invalidate];
CurrentMinuts = 0;
CurrentSeconds = 0;
//remove piece configurations
[pieceConfigMatrix removeAllObjects];
[pieceFramesMatrix removeAllObjects];
PuzzleViewController *modalView = [[PuzzleViewController alloc] init];
[self unloadObjects];
[self presentModalViewController:modalView animated:YES];
[modalView release];
}
And the unloadObjects function:
- (void) unloadObjects
{
[self resignFirstResponder];
[viewOriginalPicture release];
viewOriginalPicture = nil;
[timeLabel release];
timeLabel = nil;
[gameView release];
gameView = nil;
[originalImage release];
originalImage = nil;
[gameViewFrame release];
gameViewFrame = nil;
[timer release];
[showOutlineButton release];
showOutlineButton = nil;
}
I have a lead of what I do wrong, but I am not sure. Let me explain. I am adding the puzzle pieces to the 'gameView' property. For this, I have a 'SplitImage' object. The following function is called in - (void) viewDidLoad:
- (void) generatePuzzleWithImage:(UIImage *) image
{
SplitImage *splitSystem = [[SplitImage alloc] initWithImage:image andPuzzleSize:gPuzzleSize];
[splitSystem splitImageAndAddToView:self.gameView];
[splitSystem release];
}
Next, initialization function for the SplitImage class and the splitImageAndAddToView function:
- (id) initWithImage: (UIImage *) image andPuzzleSize: (int) pSize
{
if((self = [super init]))
{
UIImage *aux = [[[UIImage alloc] init] autorelease];
pieceCenterSize = [SplitImage puzzlePieceSizeForNumberOfPieces:pSize];
UIImage *outSideBallSample = [UIImage imageNamed:#"sampleHorizontal.jpg"]; //convexity size for puzzle size
outSideBallSample = [outSideBallSample resizedImageWithContentMode:UIViewContentModeScaleAspectFit bounds:CGSizeMake(pieceCenterSize, pieceCenterSize) interpolationQuality:kCGInterpolationHigh];
outSideBallSize = roundf(outSideBallSample.size.height);
puzzleSize = pieceCenterSize * pSize;
pieceNumber = pSize;
if(image.size.height < puzzleSize || image.size.height > puzzleSize || image.size.width < puzzleSize || image.size.width > puzzleSize)
{
aux = [SplitImage resizeImageForPuzzle:image withSize:puzzleSize];
aux = [SplitImage cropImageForPuzzle:aux withSize:puzzleSize];
}
aux = [aux imageWithAlpha];
originalImage = [[UIImage imageWithCGImage:aux.CGImage] retain];
mainImage = aux.CGImage;
imageSize = CGSizeMake(aux.size.width, aux.size.height);
NSLog(#"%#", NSStringFromCGSize(imageSize));
splitImageSize = CGSizeMake(pieceCenterSize + 2*outSideBallSize, pieceCenterSize+2*outSideBallSize);
}
return self;
}
- (void) splitImageAndAddToView: (UIView *) view
{
for (int i = 0; i < pieceNumber; i++)
for(int j = 0; j < pieceNumber; j++)
//some code
UIImage *mask;
mask = [self randomlyRetriveMaskWithPrefix:1 forPieceAtI:i andJ:j];
CGImageRef split = CGImageCreateWithImageInRect(mainImage, cuttingRect);
PuzzlePiece *splitView = [[PuzzlePiece alloc] initWithImage:[UIImage imageWithCGImage:split] andMask:mask centerSize:pieceCenterSize objectMatrixPosition:i*pieceNumber+j outSideBallSize:outSideBallSize pieceType:pieceType pieceFrame:cuttingRect];
[pieceFramesMatrix addObject:[NSValue valueWithCGRect:cuttingRect]];
[splitView setTag:(i+1)*100+j];
[view addSubview:splitView];
CGImageRelease(split);
[splitView release];
}
Thank you,
Andrei
Objective-C memory management 101 is here:
https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
Ignore the stuff on garbage collection, it isn't available for iOS.
It's not normal to release objects before presenting the next UIViewController.
Assuming you have a NIB, its contents will be loaded the first time that you (or the system) accesses the view member. Your view controller will get a call to loadView, then subsequently to viewDidLoad. If you have any views you want to add programmatically, you can do that in loadView or viewDidLoad, if you want to interrogate objects loaded from the NIB then you can do that in viewDidLoad.
Once loaded, the view will remain in memory unless and until a low memory warning occurs. At that point it'll be released. You'll get viewDidUnload. You should release anything view related that you've still got an owning reference to in there, and set to nil any weak references you may have.
Any attempt to access the view property subsequently will cause the NIB to be reloaded, etc.
So, when presenting a new UIViewController, just present it. If you create objects that are used only for display of that controller then do so on viewDidLoad, and release them on viewDidUnload.
That all being said, the Leaks tool in Instruments should be able to tell you which types of object are leaking and where you first allocated them, so it makes finding leaks really quite easy. The only thing to watch out for is that if one object handles its properties/members entirely correctly but is itself leaked then anything it creates will generally also leak. So when something leaks, check the thing that created it isn't also leaking before tearing your hair out over why you can't find a problem.
First, retainCount is useless.
Activity Monitor is pretty close to just as useless. As well, the behavior in the simulator can be quite different on the memory use front. Better to focus debugging of a problem like this on the device.
Next, this:
The problem is that none of the
UIViewController's dealloc functions
are ever called. I have decided to
unload all my retained objects
programmaticaly, right before
presenting the next UIViewController
class.
Any time you find yourself programmatically working around incorrect behavior, you are just creating more bugs.
Step back from your custom hack and figure out why the dealloc isn't being called. Something somewhere is over-retaining the object. The allocations instrument with retain tracking turned on will show you exactly where all retains and releases are sent to the errant objects.
Leaks likely won't show anything if whatever is retaining the objects is still reachable from a global or the stack (i.e. leaks are objects that can never be used by your program again -- but there are many more ways to explode memory use without it being truly a leak).
You should also "Build and Analyze", then fix any problems it identifies.
Next, if you are seeing memory accretion of repeated operations on the user's part, then Heapshot analysis is extremely effective at figure out exactly what has gone wrong.
Some specific comments:
puzzleImage = [[UIImage alloc] init];
puzzleImage = img;
2 bugs; you are leaking a UIImage and not retaining img. That your app doesn't crash in light of the above code indicates that there is likely an over-retain elsewhere.
retainCount is not a reliable debugging tool.
You should never pay attention to or rely on retainCount. Just because you released it does not mean that some part of the program does not still have a reference to it. retainCount has no value.
Generally as a rule of thumb. If you use 'alloc' you must 'release' at some point.
Unless ofcourse you have put it into an autorelease pool.
Leaks should be able to point you to the objects that are leaking, using that, narrow down to where those objects are added, stored etc.
Post examples of your code on how you instantiate objects and where you release them, you maybe doing something wrong early on.
edit: apologies, i put 'init' not 'alloc' previously, thank you dreamlax, early morning mistake.
Every time I turn the page in my app, I am removing and releasing the previous viewController - but for some reason it is still in memory. I know this, because after using the app for a while, I get 47 memory warnings - one from each view controller - if I had opened 47 pages before the memory warning occurred. I get 60 memory warnings if I had opened 60 pages before the memory warning occurred. And so on...
This is the code that runs from page to page:
UIViewController *nextController;
Class nextClass = [pageClasses objectAtIndex:(currentPageIndex - 1)];
nextController = [[nextClass alloc] initWithNibName:[pageNibs objectAtIndex:(currentPageIndex - 1)] bundle:nil];
[nextController performSelector:#selector(setDelegate:) withObject:self];
[currentPageController.view removeFromSuperview];
[self.view addSubview:nextController.view];
[currentPageController release];
currentPageController = nextController;
[currentPageController retain];
[nextController release];
Can anybody point to any issues they see?
Thanks so much!
As as aside, make sure you also nil any outlets your viewController has in viewDidUnload and generally do the opposite of any corresponding code in viewDidLoad. I see a lot of iOS code which doesn't do this and it stops the runtime properly unloading view controllers and associated views.
Have you played with the Behavior and Memory sections of the Window Attributes panel in IB's Inspector? This is where you usually control memory use and release stuff (outside of the code itself).
Try changing values for the view object in question, as well as on the window (or whatever it is for iPhone).
I believe it's because you're calling retain on currentPageController. I just recently asked a similar question and got a ton of clarification on memory management.
EDIT: What if you did something like:
[currentPageController.view removeFromSuperview];
[currentPageController release];
Class nextClass = [pageClasses objectAtIndex:(currentPageIndex - 1)];
currentPageController = [[nextClass alloc] initWithNibName:[pageNibs objectAtIndex:(currentPageIndex - 1)] bundle:nil];
[currentPageController performSelector:#selector(setDelegate:) withObject:self];
[self.view addSubview:currentPageController.view];
It cleans up the code a bit and won't leak memory.