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 :)
Related
Why is blackview always null?
#property (strong, nonatomic) UIView *blackView;
[_blackView setFrame:self.view.bounds];
[_blackView setBackgroundColor:[UIColor colorWithWhite:0 alpha:0.8]];
[self.view addSubview:_blackView];
NSLog(#"%#", _blackView); // i get null
I was instantiating it using alloc init before but i need to access this view between methods so i am using it as a property
Try using self.blackview and initialize
self.blackview = [[UIView alloc] initWithFrame:self.view.frame];
Before you do anything to the object(UIView). You must allcate momery for it, and do some initializations. Use alloc and init method. Like:
UIView *blackView = [[UIView alloc] initWithFrame:CGrectMake( , , , )];
I must say, this is something that even the beginners should know.
After you have created a property. Make sure you have synthesized it.
#synthesize blackView;
And then make sure you alloc some memory to it atleast, use
self.blackView = [[UIView alloc]init];
And then you can perform, any function on it, that you want.
#property (strong, nonatomic) UIView *blackView;
Here you have made the decalaration. But that does not mean that you have allocated any memory to it.
In your implementation file you need to allocate memory to it and initialise it as well.
If you are using XCode version 6 and above you dont need to synthesize your property because that is done by Xcode itself.
Otherwise you need to syntesize your property.
#synthesize blackView;
self.blackView = [UIView alloc]initWithFrame:self.view.bounds];
Also if you have a nib file, and there you have alraedy added a UiView and you want it to be your blackView then you have to make an IBoutlet Connection from your nib file to your .h file. in this case you dont need to initialise or allocate memory to your UIView object. That is done by iOS SDk itself.
I was digging into memory management, i found this.
I created a property button.
#property (nonatomic, retain) UIButton *button;
In ViewdidLoad method, i have written following code.
self.button = [[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)];
[self.view addsubView:self.button];
Doing XCode analyze, i am getting potential leak of an allocated variable on line 33 i.e; self.button.
Why does this happens? If i create a local UIButton and assign it to self.button and use it, then there is no potential leak. If i alloc memory to self.button or any property variables, it leaks.
Thanks
Jithen
Assigning a value to self.button calls the synthesized setter method:
- (void)setButton:(UIButton *)button;
Because you added the "retain" attribute to your property declaration, the synthesized setter will automatically call "retain" on the object that is set. This ups the retain count of the object.
Calling "alloc" on UIButton also ups the object's retain count.
So doing self.button = [UIButton alloc] essentially will up your retain count by 2. That's why there's a potential leak.
You can fix this by doing either:
self.button = [[[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)] autorelease];
or
UIButton *temp = [[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)];
self.button = temp;
[temp release];
or
_button = [[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)];
#property (nonatomic, retain) UIButton *button; using this you are retaining object.
Now using self.button = [[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)]; you are allocating memory and retain count is increased by 1.
So in you case this is a leak as retain count of object is increased. In case if you have local object and you alloc then again releasing it . So there is no extra retain count and there is no leak.
Abstarct
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.
EDIT
Now you are doing
#property (nonatomic, assign) UIButton *button;
self.button = [[UIButton alloc] init];
[self.button release];
Here you are accessing object using self which invokes on variable of your created property. You are sending +1 retain count over property object so it becomes 2 as property it self has getter and setters. So instead of doing this you can use instance variable like this.
#property (nonatomic, assign) UIButton *button;
_button = [[UIButton alloc] init];
[_button release];
Before ARC, you would normally do this for a retain variable:
UIButton* btn = [[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)];
self.button = btn; // property increases retain count because of its declaration as "retain"
[btn release];
with ARC, you would probably do this:
#property (nonatomic, weak) UIButton* button;
self.button = [[UIButton alloc] initwithFrame : CGRectMake(10, 10, 20, 20)];
[self.view addsubView:self.button];
the second example illustrates that you don't really need to have your property retain the button (via retain or strong) because when you add a subview to a view container, the containing view will retain the new child.
Of course, there are some exceptions. Sometimes, you might actually want to remove your view (button) from the superview, but not let it be released, because you'll add it back later.
So, sometimes, it's valid to retain UI objects. Usually, it's not necessary, though.
Update: I would like to comment here that this kind of problem is why Apple wants people to use ARC. This is a really basic memory management scenario, that continues to foil lots and lots of new developers. At this point, there's very little reason for beginning iOS developers not to be using ARC.
Your UIButton instance is being retained twice. [UIButton alloc] creates a retained instance and the button property is retaining it when assigned via self.button. With MRC (Manual Reference Counting) code you need to release anything you retain.
When you create the button do the following:
UIButton *button = [[[UIButton alloc] initWithFrame:...] autorelease];
self.button = button;
Alternatively, use the preferred creator method for `UIButton':
self.button [UIButton buttonWithType:UIButtonTypeCustom];
self.button.frame = CGRectMake(...);
You also need to release the button wherever you clean up objects assigned to your properties. You're coding will be much simpler if you use ARC (Automatic Reference Counting) instead of MRC.
I don't know whether you have released it or not but whenever we alloc memory, we have to release it(if ARC is not used in your project).
So just release it in dealloc like this:
-(void)dealloc {
[button release];
[super dealloc];
}
I got 30 of these codes, with the same implementation:
// .h
#interface ViewController : UIViewController{
IBOutlet UIImageView *circle;
IBOutlet UIImageView *circle2;
}
#property (nonatomic, retain) IBOutlet UIImageView *circle;
#property (nonatomic, retain) IBOutlet UIImageView *circle2;
// .m
#implementation ViewController
#synthesize circle;
#synthesize circle2;
- (void)viewDidLoad
{
circle = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"Circle.png"]];
circle2 = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"Circle.png"]];
}
And somewhere in my code, Im adding it as a subview.
My problem is,Is there a way to make it shorter, for it to be maintainable.
You can use one IBOutletCollection instead of 30 IBOutlets. You probably want to set the tag on each UIImageView though, so you can still tell them apart.
(This answer assumes you use a nib. Remove the lines where you instantiate the UIImageViews in viewDidLoad if so.)
If you use name like - Circle1.png, Circle2.png, then you can go for for loop for creating this in a loop.
Something like -
for (int i = 0; i < imageCount ; i++ ) {
circle = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:[NSString stringWithFormat: #"Circle%d.png" , i]]];
}
Is there a pattern to where you are putting these views in their superview? If so, you could use a for loop that goes to 30 and programmatically create the views and add them to their superview.
So for example:
for (i = 0; i < 100; i++)
{
UIImageView* circle = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:#"Circle.png"]];
[self.view addSubview:circle];
}
Would add 100 of the image views you would want. Of course you will need to adjust the positions of each of these views in the loop, but this is the general idea.
while creating this viewcontroller pass the frame as a parameter.thats the only thing changing rite??.the image is same and as there are no other properties .. it may work
I have two classes:
RootViewController.h
RootViewController.m
In my RootViewController.h
// in .h file
UITextField* myTextField_;
#property (nonatomic, retain) UITextField* myTextField.
In my RootViewController.m
// in .m file
#synthesize myTextField = myTextField_
// in dealloc
[myTextField_ release]
// in viewDidLoad
UITextField* tf = [[UITextField alloc] init] initWithFrame:CGRectMake(200,6,100,30)];
[nameTextField_ = tf];
[tf release]
My question is,
Does that create any memory leaks? Or will that crash? Are there better ways to create an instance of UITextField so I keep a reference to it? Perhaps
myTextField_ = [[UITextField alloc] init] initWithFrame:CGRectMake(200,6,100,30)];
would that be sufficient?
The simpliest way is to do this like this:
.h:
UITextField *myTextField;
#property (nonatomic, retain) UITextField *myTextField;
.m
#synthesize myTextField;
- (void)viewDidLoad {
myTextField = [[UITextField alloc] initWithFrame:CGRectMake(200,6,100,30)];
}
- (void)dealloc {
[myTextField release];
}
You will have one instance which is allocated and released in most clean way and you will have reference to this textfield all the time.
You should not do [tf release] as you are accessing your variable directly.
If you access it via self. notation that it will be called [tf retain] and then you should release tf. So in your current version all is ok besides line where you are releasing.
[nameTextField_ = tf];
change:
[self setMyTextField:tf]
Yes, this will do:
myTextField_ = [[UITextField alloc] initWithFrame:CGRectMake(200,6,100,30)];
You can also use this:
self.myTextField = [[UITextField alloc] initWithFrame:CGRectMake(200,6,100,30)] autorelease];
(when using the property it will retain, when using directly the member myTextField_ it won't (automatically) retain). Also alloc will set retainCount to 1 so it will eventually need to be released (in your case in dealloc method you can use either [myTextField_ release]; or self.myTextField=nil;);
Not sure what this is (I believe it will show up some compile errors):
[nameTextField_ = tf];
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.