iphone memory leaks and malloc? - iphone

Okay so im finally to the point where I am testing my iPad App on an actual iPad...
One thing that my app does is display a large (2mb) image in a scroll view. This is causing the iPad to get memory warnings. I run the app in the instruments to check for the leak.
When I load the image, a leak is detected and i see the following in the allocations:
ALl Allocations: 83.9 MB
Malloc 48.55 MB: 48.55 MB
Malloc 34.63 MB: 34.63 MB
What im trying to understand is how to plug the leak obviously, but also why a 2MB image is causing a malloc of 20x that size
I am very new to programming in obj-c so im sure this is an obvious thing, but I just cant figure it out. Here is the code:
#interface ChartsViewController : UIViewController <UIScrollViewDelegate, UIPickerViewDelegate, UIPickerViewDataSource> {
IBOutlet UIScrollView *scrollView;
UIImageView *imageView;
NSString *chart;
NSString *chartFile;
UIPickerView *picker;
NSDictionary *chartsDictionary;
NSArray *chartTypes;
NSArray *charts;
IBOutlet UILabel *chartNameLabel;
IBOutlet UIActivityIndicatorView *activityIndicator;
}
#property (nonatomic, retain) UIScrollView *scrollView;
#property (nonatomic, retain) UIImageView *imageView;
#property (nonatomic, retain) NSString *chart;
#property (nonatomic, retain) NSString *chartFile;
#property (nonatomic, retain) IBOutlet UIPickerView *picker;
#property (nonatomic, retain) NSDictionary *chartsDictionary;
#property (nonatomic, retain) NSArray *chartTypes;
#property (nonatomic, retain) NSArray *charts;
#property (nonatomic, retain) IBOutlet UILabel *chartNameLabel;
#property (nonatomic, retain) IBOutlet UIActivityIndicatorView *activityIndicator;
-(IBAction) chartSelected;
- (void)alertView:(UIAlertView *)actionSheet
///////////////////////////////
-(IBAction) chartSelected {
[imageView removeFromSuperview];
imageView = nil;
chartNameLabel.text = #"";
NSInteger chartTypeRow = [picker selectedRowInComponent:kChartTypeComponent];
NSInteger chartRow= [picker selectedRowInComponent:kChartComponent];
chart = [self.charts objectAtIndex:chartRow];
chartFile = [chart stringByReplacingOccurrencesOfString:#" " withString:#"_"];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
NSString *tempString = [[NSString alloc]initWithFormat:#"%#/%#.jpg",docsPath,chartFile];
NSData *temp = [NSData dataWithContentsOfFile:tempString];
if (temp != NULL){
temp = nil;
[imageView removeFromSuperview];
imageView = nil;
UIImageView *tempImage = [[UIImageView alloc]initWithImage:[UIImage imageWithContentsOfFile: tempString]];
[tempString release];
self.imageView = tempImage;
scrollView.contentSize = CGSizeMake(imageView.frame.size.width , imageView.frame.size.height);
scrollView.maximumZoomScale = 4.0;
scrollView.minimumZoomScale = .05;
scrollView.clipsToBounds = YES;
scrollView.delegate = self;
scrollView.zoomScale = .3;
[scrollView addSubview:imageView];
[tempImage release];
imageView = nil;
chartNameLabel.text = chart;
}
else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Download Chart"
message:#"It appears that you have not yet downloaded this chart. Press OK to download this chart to your iPad. Depending on your internet connection, the download could take several minutes."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:#"Cancel", nil];
[alert show];
[alert release];
}
}
- (void)dealloc {
[imageView release];
[scrollView release];
[chartsDictionary release];
[picker release];
[chartTypes release];
[charts release];
[super dealloc];
}

You say you have a 2MB image. But that means a 2MB JPG, right?
So what is the size in pixels - because when you load the image into memory, it must be de-compressed. And that means it will be horizontal resolution * vertical resolution * 8 * 4 (alpha channel) bytes in memory.
That is why you are seeing 20-30MB allocated every time you load the image, regardless of retain issues (which just mean each 30MB allocated would not be released).

tempImage is still leaking.
replace the line that says
imageView = nil;
with
[self setImageView: nil];
or
self.imageView = nil;

Lots of leaks in this code.
For every object created using alloc, you need to release it at some point when you are done using it.
The following items are being leaked and need to be released
tempString
tempImage
alert
Also, you don't need that NSAutoreleasePool, one is created for you by the cocoa framework before the event that calls your IBAction is called and is drained with the method finishes. Also the autorelease pool is also only responsible for items that have been placed in it, which include anything that you send a autorelease message to and any objects you get back from a method other than alloc, new, or one with copy in the title.
Also, know that setting a local variable to nil is not the same as releasing it.
For example the creating the image should be
UIImageView *tempImage = [[UIImageView alloc]initWithImage:[UIImage imageWithContentsOfFile: tempString]];
self.imageView = tempImage;
[tempImage release];
Edit:
One more thing. When you access imageview without using self.imageview you are accessing the ivar directly and not through the property. So when you do self.imageview = tempImage, it retains the image view as it should, but when you do imageview = nil it nils out the reference without releasing the memory. This is another leak. Try self.imageview = nil instead.
As to why it is so much memory, this I do not know unless it is related to expanding the image to its full size (by pixels), and not its compressed jpg size, or to other data being leaked along with the UIImageView object.

Both of the following lines will retain the UIImageView allocated as tempImage.
self.imageView = tempImage;
[scrollView addSubview:imageView];
add this line after addSubview:
[tempImage release];
You do not need imageView as a member unless you are manipulating it later. If you keep it, be sure to release it in dealloc. In general, every property marked retain should be released in dealloc unless you have some specific reason not to.
I usually do not make properties or even have members for views that will live in the view hierarchy unless I need to manipulate them, for example changing the text of a UILabel or the state of a UIButton.

Would be a good idea to switch to ARC. Alternatively, build with the static analyser and fix all the warnings that it gives you. It is quite good at that.
It seems that you sometimes use imageView and sometimes self.imageView. That indicates that your instance variable is imageView and not _imageView. That's a huge source of bugs. If imageView is a (release) property, self.imageView = nil releases it, but imageView = nil doesn't. I strongly suggest starting all instance variables with an underscore so that you only access them intentionally.

Related

Program received signal:EXC_BAD_ACCESS. What to do with this?

I just recently started learning Objective C/Cocoa and I know how important the memory management is and I believe this error I've been having is regarding to that.
I have a very very simple screen: two UITextView, one Button, one UILabel.
My header file has:
#interface PontaiViewController : UIViewController {
UITextField *loginField;
UITextField *passwordField;
UILabel *userID;
}
#property (nonatomic, retain) IBOutlet UITextField *loginField;
#property (nonatomic, retain) IBOutlet UITextField *passwordField;
#property (nonatomic, retain) IBOutlet UILabel *userID;
- (IBAction) btnLoginClicked:(id) sender;
The implementation has:
#implementation PontaiViewController
#synthesize loginField;
#synthesize passwordField;
#synthesize userID;
-(IBAction) btnLoginClicked:(id)sender {
NSString *string1 = #"username=";
NSString *string2 = [string1 stringByAppendingString:(loginField.text)];
NSString *string3 = [string2 stringByAppendingString:(#"&password=")];
NSString *post = [string3 stringByAppendingString:(passwordField.text)];
NSLog(#"The post is %#", post);
userID.text=loginField.text;
[string1 release];
[string2 release];
[string3 release];
[post release];
}
and it finishes with
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.loginField=nil;
self.passwordField=nil;
self.userID=nil;
}
- (void) dealloc {
[super dealloc];
[loginField release];
[passwordField release];
[userID release];
}
When I run this demo, and try to write something in the TextView, I get this error.
What could it be?
Regards,
Felipe
Also, your NSStrings are autoreleased, and then you're releasing them again (over releasing). Read up on memory management of convenience methods.
stringByAppendingString returns an autoreleased object, don't release string1, string2, string3 and post.
In viewDidUnload you set loginField to nil, then you try to release it in dealloc. This isn't right. You only need to release valid items that you own.
Additionally, (as pointed out in a comment) you need to put [super dealloc] at the end of the dealloc function.
As pointed out by others, you also should not release the strings you're getting from stringByAppendingString.
Here are some basic rules about how to manage memory in Objective-C under iOS:
https://developer.apple.com/library/ios/#documentation/general/conceptual/devpedia-cocoacore/MemoryManagement.html
One thing you will find is that you only release stuff you are responsible for, and you're not responsible for it unless it was created with one of these:
alloc, allocWithZone:, copy, copyWithZone:, mutableCopy, mutableCopyWithZone
You should comment out the following
//[string1 release];
//[string2 release];
//[string3 release];
//[post release];
since you are using helper methods and not explicitly allocating anything.

NSMutableArray, with UIImage and NSString objects, unable use the objects

I'm new to xCode and objective-c, and trying to use different classes to familiarize myself with the language.
I was trying to use an NSMutableArray to show several pictures and a corresponding textsnippet to tell about the picture. I have a segmentedControl to switch between several choices. Using an UIImageView to show a picture, and a UITextView the corresponding text.
But for some reason they will not load properly? The picture and the text isn't shown. What am I doing wrong?
#interface TestingViewController : UIViewController{
UIImageView *pic;
UITextView *description;
UISegmentedControl *choice;
NSMutableArray *infoDescription;
}
#property (nonatomic, retain) IBOutlet UIImageView *pic;
#property (nonatomic, retain) IBOutlet UITextView *description;
#property (nonatomic, retain) NSMutableArray *infoDescription;
#property (nonatomic, retain) IBOutlet UISegmentedControl *choice;
- (IBAction) segmentedControllIndexChanged;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
infoDescription = [[NSMutableArray alloc] init];
}
UIImage *miko = [UIImage imageNamed:#"miko.jpg"];
NSString *mikoString = #"Info about Miko";
UIImage *alex = [UIImage imageNamed:#"alex.jpg"];
NSString *alexString = #"Info about Alex";
[infoDescription addObject:miko];
[infoDescription addObject:mikoString];
[infoDescription addObject:alex];
[infoDescription addObject:alexString];
- (IBAction)segmentedControllIndexChanged{
switch (choice.selectedSegmentIndex) {
case 0:
[pic setImage: miko];
[pic setImage: [infoDescription objectAtIndex:0]];
[description setText: [[infoDescription objectAtIndex:1] stringValue]];
break;
case 1:
[pic setImage: [infoDescription objectAtIndex:2]];
[description setText: [[infoDescription objectAtIndex:3] stringValue]];
break;
default:
break;
}
}
Thanks for any help out there.
NSMutableArray should not be an IBOutlet ..
Change this
#property (nonatomic, retain) IBOutlet NSMutableArray *infoDescription;
to
#property (nonatomic, retain) NSMutableArray *infoDescription;
and make sure you synthesize it..
and also the program isn't working since your array is not initialized and is not adding objects to it.
put this in ViewDidLoad
infoDescription = [[NSMutableArray alloc] init];
Your infoDescription array is probably nil. Put this before you add items to it:
infoDescription = [[NSMutableArray alloc] init];
Also, infoDescription doesn't need to be declared as an IBOutlet. IBOutlets are only for properties that you bind in Interface Builder (usually views).

Problem With NSArray

i have the following code
#interface TestYourInfoViewController : UIViewController {
IBOutlet UIImageView * questionImage;
NSArray *historyQuestions;
int questionHistoryNo;
}
#property(nonatomic,retain) UIImageView * questionImage;
#property(nonatomic,retain) NSArray *historyQuestions;
#property int questionHistoryNo;
-(IBAction)solution:(id)sender;
#end
- (void)viewDidLoad
{
NSArray* array = [[NSArray alloc]init];
historyQuestions = array;
historyQuestions=UmRandomOrder(49, 1, 0);
questionImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"h%#.jpg",[self.historyQuestions objectAtIndex:0]]];
[array release];
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(IBAction)solution:(id)sender{
questionHistoryNo= questionHistoryNo+1;
questionImage.image = [UIImage imageNamed:[NSString stringWithFormat:#"h%#.jpg",[self.historyQuestions objectAtIndex:questionHistoryNo]]];
}
when i press the action button it gives me exception in the line [self.historyQuestions objectAtIndex:questionHistoryNo]
i believe the problem is in the nsarray somehow but i don't know what is it.. the exception is
can anyone help me ..
Actually DarkDust has it correct: the source code for UMRandomOrder shows that it properly returns an autorelease NSMutableArray. So, just change the first three lines of your viewDidLoad from:
NSArray* array = [[NSArray alloc]init];
historyQuestions = array;
historyQuestions=UmRandomOrder(49, 1, 0);
to just:
self.historyQuestions=UmRandomOrder(49, 1, 0);
And you'll be fine.
To be specific, there's no need to alloc/init/assign an array you're about to write over, and by using the property setter (self.historyQuestions = ), you'll automatically do a proper retain, as well as avoiding a potential memory leak. That also explains why it works in viewDidLoad (the autoreleased UmRandomOrder is still valid), but not in the action button (it has since been released).

Objective-c - Retain Count when I change View go to zero

I declared a UIImage and a UIImage View in one viewcontroller like this:
In the .h file:
UIImageView* itemImageView;
UIImage* itemImage;
#property (nonatomic, retain) UIImage* itemImage;
#property (nonatomic, retain) UIImageView* itemImageView;
In the .m file:
#synthesize itemImage, itemImageView;
In another view, I set its value:
UIImage *image = [UIImage imageNamed:#"name1.png"];
imgView.itemImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 82, 166)];
imgView.itemImageView.image = image;
[self.parentViewController.view addSubview:imgView.itemImageView];
[self dismissModalViewControllerAnimated:YES];
Inside this method, the retain count of itemImageView is 2.
But when I go back to the view where I put the property and the synthesize, the retain count is 0 and I cannot access the object.
Any idea whats happening?
Your code here looks ok. (Aside from a memory leak) You're assigning an ImageView with a retain-count +1 to itemImageView, which will increase it to two. You need to call release on your ImageView after setting it to itemImageView:
UIImageView* iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 82, 166)];
imgView.itemImageView = iv;
[iv release];
However, this doesn't fix your problem (it will even make it worse..)
Can you show more code? Have you tried stepping through it with the Debugger?
UIImageView* itemImageView;
...
#property (nonatomic, retain) UIImageView* itemImageView;
...
imgView.itemImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 82, 166)];
This causes a double retain since alloc increases the retain counter by 1, and so does the retain setting of the #property.
The general pattern for setting properties is like so:
.h:
SomeClass* someClass;
...
#property (nonatomic, retain) SomeClass* someClass;
.m:
SomeClass* temporarySomeClass = [[SomeClass alloc] init];
self.someClass = temporarySomeClass;
[temporarySomeClass release];
...
Here, we're using a temporary variable to hold the object we alloced, and then doing a release right after.
You'll see that in the Apple example code for sure.
your problem is with this line:
[self.parentViewController.view addSubview:imgView.itemImageView];
this should most likely be edited as such:
[self.parentViewController.view addSubview:self.parentViewController.itemImageView];
this leads to the question of whether imgView is really equivalent to parentViewController.view. if you wanted it to be, then you need to figure out where that got assigned and see where you messed that up. if not, then theres no point in using it for anything but a temporary container to build ur objects in before assigning it.
edit: the memory leak is a separate issue yes, but im not sure how u declared imgView in the method so i left that for you to solve :)

IBOutlet, use of member properties or not? memory leak?

I have noticed that the memory usage of a program I wrote, keep increasing over time. XCode's instruments shows no memory leak, yet you can see the heap stack growing over time..
Upon investigation, a lot of the memory usage come from the IBOutlet UI objects. The interface is build with Interface Builder.
Typical usage would be like:
header:
#interface HelpViewController : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *webView;
IBOutlet UIBarItem *backButton;
IBOutlet UIBarItem *forwardButton;
NSString *URL;
IBOutlet UIActivityIndicatorView *spin;
}
#property (nonatomic, retain) NSString *URL;
And for the usage :
- (void)webViewDidStartLoad:(UIWebView *)mwebView {
backButton.enabled = (webView.canGoBack);
forwardButton.enabled = (webView.canGoForward);
[spin startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
backButton.enabled = (webView.canGoBack);
forwardButton.enabled = (webView.canGoForward);
[spin stopAnimating];
}
Looking at the heap stack, you find that the UIActivityIndicatorView *spin object isn't properly deallocated, and its memory footprint will keep growing.
However, if I change the code to:
header:
#interface HelpViewController : UIViewController <UIWebViewDelegate> {
IBOutlet UIWebView *webView;
IBOutlet UIBarItem *backButton;
IBOutlet UIBarItem *forwardButton;
NSString *URL;
UIActivityIndicatorView *spin;
}
#property (nonatomic, retain) NSString *URL;
#property (nonatomic, assign) IBOutlet UIActivityIndicatorView *spin;
And in the code I do:
synthesize spin;
- (void)webViewDidStartLoad:(UIWebView *)mwebView {
backButton.enabled = (webView.canGoBack);
forwardButton.enabled = (webView.canGoForward);
[self.spin startAnimating];
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
backButton.enabled = (webView.canGoBack);
forwardButton.enabled = (webView.canGoForward);
[self.spin stopAnimating];
}
Nothing more, nothing else, then the heap stack doesn't grow anywhere as much.. and the UIActivityIndicatorView object doesn't leave any stuff behind
I can't figure out why it would make a difference here having an assign property or not, it just doesn't make sense ! Unless I massively misunderstood what's happening.
Any explanations would be welcomed..
Thanks
You need to release the objects in the dealloc method:
-(void)dealloc {
[webView release];
[backButton release];
[forwardButton release];
[URL release];
[spin release];
[super dealloc];
}
The reason why the problem doesn't occour in your second version is, that you set the property with the attribute assign, usually you should use retain for "object-properties" assign is usually used for the basic datatypes like int, float, bool etc...
EDIT:
to the part with retain and assign, afaik the behaviour is the following:
If the property is made with assign, then setting
self.thatVariable = something;
would be the same as:
thatVariable = something;
if you used retain it would be the same as:
[thatVariable release];
thatVariable = [something retain];
So if you used assign for variables that hold pointers to objects you can't be sure, that your object isnt deallocated somewhere else, which would result in a bad access.
Afaik the only reason to use assign with object is to get a weak reference. If you have to object which would both retain each other, none of it would ever get released. so thats a spot where you would use assign for objects. (f.e. often in the delegate-pattern. the object would retain it's delegate and the delegate would retain the object. In this case the delegate is often assigned)