I have a UIImageView property in my view controller set to "assign". Every time I hit a button, I set the image in the view to something new. Due to the way I am using another component, I have to removeFromSuperView and release the UIImageView every time I change images and then re add it. This doesn't seem to be contributing to the memory problem-- when using the existing UIImageView and not releasing it the memory still goes up (but there are sizing issues, which is why I am recreating it).
For some reason, using setImage will increase the memory usage of the app by about 0.8mb (the size of the image). When I do NOT set an image, the memory seems to stay relatively constant. Any ideas why?
[self.pictureView setImage: img];
[self.pictureView setFrame: CGRectMake(0,0,[img size].width, [img size].height)];
[img release];
I think you are leaking the UIImageView, and when you leak the UIImageView, you are leaking the image in it.
It depends how you recreate the UIImageView. Be sure that you are releasing the old image view before setting the new one to the property.
It may be best to set the property as retain, because when you set new value to retain property, it releases the old instance.
Maybe set the UIImageView to nil before setting the image to clear out the reference?
pictureView.image=nil;
Related
How do you move a UIWebView offscreen?
Is that determined by the CGRect? Or some other property of UIView or UIWindow?
You can try:
[webView setFrame:CGRectMake(320.0f, 0.0f, 320.0f, 480.0f)];
or
[webView removeFromSuperview];
The first one just move it offscreen, you just cannot see it. The second one remove it from superview, maybe it still exist in memory, or maybe it'll removed when received memory warning.
UIWebView is a subclass of UIView (as are most visible objects in cocoa touch) which means you can use setBounds or setFrame in order to 'move' the view. If you use negative or large positive numbers the view will be moved offscreen.
Do remember that things offscreen also take up memory ;-)
Yes, you can do that. You could change the frame property of UIWebView to some coordinates which are off the screen.
You could do this but if you are done with the UIWebView & simply want to close it then this might not be the best solution as the memory allocated is still not released. First animate it off the screen then release it to free the memory. To release it do [YourWebView removeFromSuperview]; [YourWebView release]; YourWebView=nil;.
You can hide it by setting the hidden property to YES.
You can also move it off the screen with the frame, as you mentioned. A good place to start is CGRectMake(self.superview.frame.size.width,self.superview.frame.size.height,...) though it won't do well with rotations.
You can also set the alpha to 0, which is akin to making it hidden.
You can also send it the method removeFromSuperview.
There may be more ways.
Enjoy,
Damien
I've learnt that if you create an object, you own it and need to release it when you're done with it. In this case I create an UIImageView and add it to my view like this:
myImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
[myImageView setFrame:CGRectMake(10,10,100,100)];
[self.view addSubview:myImageView];
[myImageView release];
If I later on want to detect touch on myImageView like this in my touchEnded-method:
if([touch view] == myImageView){
NSLog(#"TOUCHED!");
}
This works, but is this correct, since I now used myImageView after releasing it?
And how do I release myImageView from the self.view that I added it to earlier?
The cardinal rule of Cocoa memory management is that you should retain those things you care about, and release those things you do not care about. There are a very small number of exceptions to prevent retain loops (delegates and datasources are never retained), but this is the rule you should follow.
In this case, if you are storing the image in an ivar, I would continue to retain it, regardless of the fact that its superview will always retain it, and so you don't "have" to. But if the view is removed from its superview, you will wind up with a dangling pointer, and then you will crash, so I code defensively against that by retaining. If you used an accessor here (which you should), then this would be automatic and much safer.
Apple has grown more consistent about this in iOS, switching their recommendation about IBOutlets. On Mac, you do not retain your IBOutlets, but in iOS, Apple explicitly instructs you to do so. This is similar to the situation you are discussing, and I agree with Apple's move towards a safer approach.
As long as your myUIImageView object has a retain count > 0, it will still exist and you can continue using it. When you first add it as a subview, it gets a retain message so it's retain count is likely 2. Then you send it release, so its retain count is reduced to 1. This means it still exists in memory. Now, if you sent it release again, or sent it removeFromSuperView then its retain count would be zero, and you'd lose it.
The behavior is erratic, sometimes you may see it works, sometimes you get a crash.
If you want to use that variable to point to your image view, retain the ivar (by using a retain property). This way you ensure that the image view is available for your controller class to use.
Assuming myUIImageView is an ivar of your custom UIView subclass, your code will work as long as your image view stays in his superview. The image view instance may be deallocated and you may end with an invalid pointer referencing a deallocated object. At best you crash.
According to memory management guidelines, you should consider your image view <-> custom uiview subclass relation as wether:
strong reference. You own the image view (as you created it), and are responsible for retaining / releasing it
weak reference. You don't own the object, thus keeping a reference to it may be dangerous.
in your case, it's probably a strong reference. your myUIImageView ivar should be a nonatomic retained property of your object.
If you need to access your UIImage at some point in the future, you need to retain it.
How you do this is at your discretion, but you should not rely on the UIView to retrain your objects for you. The situation you've created works for now, but it's fragile.
I have a UIImage that I use throughout my app as a background image for grouped UITableViews.
I thought that for efficiency I would alloc and init the UIView with my UIImage in my appDelegate and then access throughout my app. That way I would only allocate that imageView once and if I was drilling into a nav stack with multiple tableviews with this image I wouldn't need to worry about releasing and restoring the image as I descend and ascend or incur overhead at each step.
As soon as I tried this I noticed that it seems that the UITableView class is releasing the my shared image down to 0 and it therefore is going away. Makes perfect sense but I would need to prevent the image from ever hitting a 0 retain count for this to work.
Is this a totally goofy approach?
If it is not what's the best way to retain my shared ImageView? I know I could call retain when I setup each tableview's backgroundimage but I was wondering if there is a way to set the retain count of the shared UIImageView to NSUIntegerMax in my appDelegate. I've setup singleton classes before but in this case I'm trying to have a single property that is never released rather than creating a UIImageView singleton subclass.
Sorry if that's a little muddled and thanks for any pointers.
I would not worry so much as + (UIImage *)imageNamed:(NSString *)name are cached.
From the spec:
This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.
My UIButton has it's background image set like this:
[myImageButton setBackgroundImage:[UIImage imageNamed:myImageName] forState:UIControlStateNormal];
myImageButton is a retained property of the class, and is setup with IB. No where else is it accessed in the app.
myImageName is simply an NSString with a filename like #"myImage_number_1.png"
I am loading large images, 1024 x 1024 in size. When the view is shown, it changes the image with the above statement, then available memory decreases.
After I see the view about 7-9 different times, the app crashes with a memory warning.
I thought the method would free up the loaded image.
The view itself is only instantiated and allocated one time, so it's not in the retain/release cycle if the view controller.
Is there something about this setBackgroundImage I don't know that causes it to not release memory?
Ah, found it. Every time imageNamed is used to load an image, it caches the image in memory. I switched to imageWithContentsOfFile - it doesn't cache images.
To future coders, #just_another_coder answer is correct, but there's something you all should know.
[UIImage imageNamed:myImageName] loads the image in a special system cache, and then future calls with that image path will return the image in the cache instead of reloading it from disk.
[UIImage imageWithContentsOfFile]simply loads the image at the path you specify, but does no caching. Multiple calls to imageWithContentsOfFile for the same image will result in multiple copies in memory.
So you should think about which one you'd rather, and if you use imagewithcontentsoffile you should remember to nil out that button otherwise you'll be doomed to an ever growing app (memory usage wise)
Hey all, here's the deal...
I've got a UIImage that is being loaded in the background via an NSURLConnection within a subclassed UIImageView. Until the data finishes downloading, a placeholder image is shown. The UIImage needs to be used by another, separate UIImageView, as well.
The problem I'm having is that if I set the second UIImageView's image property to that of the subclassed object before the download is complete, the second UIImageView never displays the downloaded image, since it's image prop is pointing to the placeholder.
Is there anyway to pass a pointer to a pointer between these two UIImageViews?
I've tried things like this:
imageView2.image = &[imageView1 image];
imageView2.image = *[imageView1 image];
But these don't seem to work. Any ideas?
Those pointers look particularly gross. The problem with doing it that way is that, even though you might be updating the data pointed to by those pointers, you are not notifying your UIImageView subclasses that the data has changed, and thus they don't know to redraw.
Instead of playing with that pointer mess, why not use Key-Value Observing instead? If you have a separate thread running that downloads the UIImage, you can just tell your UIImageViews to observe that property and when your downloader class is finished downloading all the data and has put it into the property, the views will get a notification and can then display the image.
Can't you just catch the connectionDidFinishLoading: message from the NSURLConnection and then set the second image?