Yes, I know that my problem is not the unique, but I have analysed many source codes but nothing works well.
I am creating an array of 100 images which will be used for UIImageView animation and after finishing animation I want to free the memory.
.h
IBOutlet UIImageView* 1mage;
.m
NSMutableArray* 1mageArray = [[NSMutableArray alloc]initWithCapacity:99];
for (int i=1; i<=100; i++) {
[1mageArray addObject:[UIImage imageNamed:[NSString stringWithFormat:#"1mage_%.4d.png",i]]];
}
1mage.animationImages = 1mageArray;
1mage.animationDuration = 8.0;
1mage.animationRepeatCount = 0;
[1mage startAnimating];
[1mageArray release];
I have read Memory Management Guide, and seems, like I am doing everything okay, but debugger does not agree with me, and after playing the animation I still have 60 MB used.
Thanks in advance!
copy/paste , and I shorted variables by replacing the long text with 1. do not pay attention on this!
Your image will retain the data when you you set the animationImages. After all, it needs those images, doesn't it?
If you release it or set its animationImages property to nil, they might be released...
Related
I am having trouble with memory building up and am not able to empty it once I am done with it. When I look at the diagnostic tool ": Allocations: Instruments: Object summary: statistics", the memory is just continuously building up.
example:
for (int i=0; i<100000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
// tried each of the following
//[lblPost dealloc]; //neither work to clear memory it just builds
//[lblPost release]; //
}
--> Do I need to seperate CGRect out and clear that.
--> (I know I can just keep writing to one label, this is a simplified version where in the bigger version, one label would not work so easily. )
--> (I find it hard to believe that I can not create an object and then destroy it 10000 or 100000000 times over. In standard C, I can accomplish this with memory-blocks by using "free()" )
The view you are adding your label to is retaining it, that's why each none of the labels is deallocated (even if you send release message)
Maybe i really don't understand what you're trying to do, but your each indiviual object you'r allocating is retained in the view. Let me try to explain it in the code:
for (int i=0; i<10000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
// lblPost now has a retain count of 1, as you alloc'd it, you'll have to release it!
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
// lblPost now has a retain count of 2, as adding to the view adds a reference to it
[lblPost release]
// you alloc'd it, now you should release it. it now has a retain count of 1, which means it's in the ownerhsip of the self.view
}
Now, when you release or free self.view, the lblPost objects should be released as well
Why are you allocating memory for 10000 UILabels and adding them as a subview exactly? That's iPhone torture. The items in bold cause your surge in memory. Plus you're releasing none of them.
Also - never ever ever call dealloc yourself - dealloc is called automatically when you release something.
This is fun! I have decided to jump in. I posted this in my comments but here it is again:
I think madhu misunderstood the [lblPost release]. This "release" only applies to the lblPost instance. Not the ones retained by self.view... etc. So you still have 10000 label retained by self.view...
So, you create 10000 instances of lblPost and then release all of them (10000) by this line [lblPost release] in your for loop. That is just fine. But then in your for loop you also have this line [self.view addSubview: lblPost]. This line will add 10000 instances to your self.view. And they are the reason why your system crashed.
for (int i=0; i<10000; i++){
UILabel *lblPost = [[UILabel alloc] initWithFrame:CGRectMake(x,y,w,d)];
[lblPost setText: "Hello World"];
[self.view addSubview: lblPost];
[lblPost release];
//You should release after adding it to your view
}
So I am making an app really quick, and even though it isn't the best solution, it appears to be working for the most part. So I have a simple image view that pulls an image out of an NSMutableArray arr. I create the array and populate it inside of ViewDidLoad in this manner:
arr = [[NSMutableArray alloc] init];
[arr addObject:[UIImage imageNamed:#"181940jpg"]];
[arr addObject:[UIImage imageNamed:#"168026.jpg"]];
[arr addObject:[UIImage imageNamed:#"168396.jpg"]];
[arr addObject:[UIImage imageNamed:#"168493_.jpg"]];
ANd I continue doing that for 130 images. Obviously this is a big array. I don't know if this is like a really bad way to do it, but if it is, I am open to suggestions! As I go through the array with some simple back and forward buttons pulling images out of the array based on a simple counter variable things work ok until image 45-ish. The app breaks down and the console says this:
* ERROR: ImageIO 'ImageProviderCopyImageBlockSetCallback' header is not a CFDictionary...
Would it help if I broke my images up into separate arrays? Am I just putting too much into the array? What am I missing here, trust me, I am all ears.
Thanks
EDIT: Here is some more information
This is how I am sifting through the array, using a UISegmentedControl set on the top of the screen:
-(void) pickedOne{
if(segmentedControl.selectedSegmentIndex == 1){
NSLog(#"hey");
if(position < [arr count]-1){
position++;//This is my global counter variable
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
}
}else if(segmentedControl.selectedSegmentIndex ==0){
if(position >0){
position--;
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
}
}
}
It doesn't appear to be a problem with memory management, but then again, I don't consider myself a pro at that by any means...
In terms of the mutable array, what you put into it is just a pointer to some other object. It is those other objects you have to worry about and, yes, you have too many of them (more likely than not).
ERROR: ImageIO 'ImageProviderCopyImageBlockSetCallback'
header is not a CFDictionary...
Would it help if I broke my images up
into separate arrays? Am I just
putting too much into the array? What
am I missing here, trust me, I am all
ears.
That sounds more like you have an over-release problem and are passing something bogus to the ImageIO APIs.
And, in fact, that is exactly what you have:
UIImage * img = [arr objectAtIndex:position];
[imageView setImage:img];
[img release];
That release is spurious; it does not balance a retain anywhere in your code. objectAtIndex: does not return a retained object and the UIView will take care of retaining/releasing the image internally.
Remove that release (and the other one, too)!
You still need to worry about memory consumption. At a size of 42K each (not an unreasonable size, but entirely made up), 130 images will weigh in at ~6MB or so, prior to any decompression or other expansion that occurs as a part of storage.
The devices are quite memory constrained.
I've read a lot of UIScrollView with UIImageView threads here or other googled pages. But I still cannot get the problem I'm confronting. I'm having a cold right now. Hope I can still make it clear, lol. Here is the problem:
I'm building one app which mainly uses UIScrollView to display a few images. Here the amount counts, not the size, which is averagely 100KB(I even converted PNG to jpg, not sure whether it helps or not). With no more than 10 images, my app crashes with memory warning. This is the first time I encounter memory issue, which surprised me as the compiled app is less than 10MB.
At the very beginning, I load all the images on launch, looping all names of image files and do
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:imgName]];
[scrollview addSubview:imageView];
[imageView release];
If I'm right, I think after launch, all the images are in memory, right? But the funny thing here is, the app could launch without any problem(at most a level 1 memory warning). After I scroll a few images, it crashed. I checked leaks for sure and also allocations. No leak and allocation almost had no change during scrolling.
So, is there something special done by imageNamed rather than cache?
And then, yes, I turned to lazy load.
For fear of checking page and loading images on demand might jerk the scrolling(which was proved true), I used a thread which runs a loop to check offset of the scroll view and load/unload images.
I subclassed UIImageView by remembering the image name. It also contains loadImage and unloadImage which will be executed on that thread.
- (void)loadImage {
/if ([self.subviews count] == 0) {
UIImageView iv = [[UIImageView alloc] initWithImage:[UIImage imageNamed:self.imageName]];
[self performSelectorOnMainThread:#selector(renderImage:) withObject:iv waitUntilDone:NO];
//[self addSubview:iv];
[iv release];
}*/
if (self.image == nil) {
//UIImage *img = [UIImage imageNamed:self.imageName];
UIImage *img = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[self.imageName stringByDeletingPathExtension] ofType:[self.imageName pathExtension]]];
// image must be set on main thread as UI rendering is main thread's responsibility
[self performSelectorOnMainThread:#selector(renderImage:) withObject:img waitUntilDone:NO];
[img release];
}
}
// render image on main thread
- (void)renderImage:(UIImage*)iv {
//[self addSubview:iv];
self.image = iv;
}
(void)unloadImage {
self.image = nil;
//[(UIView*)[self.subviews lastObject] removeFromSuperview];
}
You can see the commented code that I've played with.
In unloadImage, if I write [self.image release], then I get EXC_BAD_ACCESS, which is unexpected, as I think alloc and release are matched here.
The app still crashes with no leak. The initWithContentsOfFile version even crashed earlier than imageNamed version, and made the scrolling not that smooth.
I run the app on device. By checking allocations, I found imageNamed version used much less memory than initWithContentsOfFile version, though they both crash. Instruments also showed that the allocated images were 2,3 or 4, which indicated the lazy load did do his job.
I checked PhotoScroller of WWDC2010, but I don't think it solvs my problem. There is no zooming or huge picture involved.
Anybody helps! Thank you in advance.
The crash log says nothing. The app crashes mostly after memory warning level = 2. And if run on simulator, there will be no problem.
It doesn't matter which format do you use for your images. They're converted to bitmaps when you display them.
I'd suggest to use the technique similar to that one which is used by UITableView (hide the image and free the memory it uses when it disappears from the screen and instantiate the image only when you need to show it).
As an alternate way – if you need to show these images in a grid – you might take a look to a CATiledLayer.
Anyhow, loading all the images to the memory is not the best idea :)
You can load all the images to an array. And you can design a view having one image view and try the below code:
array name: examplearray and view name :exampleview
-(void)updateImagesInScrollView
{
int cnt = [examplearray count];
for(int j=0; j< cnt; ++j)
{
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"exampleview"
owner:self
options:nil];
UIView *myView = [nibContents objectAtIndex:0];
exampleview * rview= (exampleview *)myView;
/* you can get your iamge from the array and set the image*/
rview.imageview.image = yourimage;
/*adding the view to your scrollview*/
[self.ScrollView addSubview:rview];
}
/* you need to set the content size of the scroll view */
self.ScrollView.contentSize = CGSizeMake(X, self.mHorizontalScrollView.contentSize.height);
}
Ok, I have the following very simple animation composed of 25 frames in PNG format. Each frame is 320 × 360 and about 170Kb in size. Here is the code I use
.h:
IBOutlet UIImageView *Animation_Normal_View;
In Interface Builder I have a UIImageView with a referencing outlet pointing to this. All my images are named normal_000_crop.png, normal_001_crop.png, normal_002_crop.png,...
.m:
Animation_Normal = [[NSMutableArray alloc] initWithCapacity:25];
for (int i = 0; i < 25; i++)
{
[Animation_Normal addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"normal_%03d_crop.png", i] ofType:nil]]];
}
Animation_Normal_View.animationImages = Animation_Normal;
Animation_Normal_View.animationDuration = 1; // seconds
Animation_Normal_View.animationRepeatCount = 0; // 0 = loops forever
[Animation_Normal release];
[self.view addSubview:Animation_Normal_View];
[Animation_Normal_View startAnimating];
On the simulator everything loogs good visual animation start as soos as the startAnimating is issued.
But on the iPhone 3G running iOS 4.0.2, the visual animation starts a good 2 to 3 seconds after the startAnimating is issued.
I have tried about every technique on I could find in blogs or forum that should solve this to no avail.
Any hints appreciated even if it's a completly different way to to a PNG based animation.
Thanks.
This is a good question and I will address it here with a few thoughts.
First, you are loading a series of graphics that are around 4MB in total size. This may take a moment, especially on slower (older) devices.
In the #interface block of your .h file you may want to declare two properties such as:
IBOutlet UIImageView *animationViewNormal;
NSMutableArray *animationViewNormalImages;
The first is the UIImageView that you already have (just renamed for best practices) and the second is a mutable array to hold the stack of images for the image view. Let me state that if having "normal" implies state. For clarification, are you loading additional sets of images for different states?
In your .m file in the #interface create the following method:
- (void)loadAnimationImages;
This will provide the function to lead the image stack to the mutable array defined in the header.
In the same .m file in the #implementation you'll want the following:
- (void)loadAnimationImages {
for (NSUInteger i = 0; i < 23; i++) {
NSString *imageName = [NSString stringWithFormat:#"normalCrop%03u", i];
UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:imageName ofType:#"png"]];
if (image) {
[animationViewNormalImages addObject:image];
}
}
}
As you can see I renamed the PNG files from normal_%03u_crop to normalCrop%03u as it is best practice to put the index label at the end of the file name (also most apps will output the content this way). The loop loads an image, checks to see that it is an image and then adds the image to the "image stack" in the mutable array.
In the init() you'll need the following:
- (id)init {
...
animationViewNormalImages = [[NSMutableArray alloc] init];
...
}
This allocates the (animationViewNormalImages) mutable array to hold your stack of images for the image view.
We'll now move on to the code for the viewDidLoad():
- (void)viewDidLoad {
[super viewDidLoad];
...
[self loadAnimationImages];
[animationViewNormal setAnimationImages:animationViewNormalImages];
[animationViewNormal setAnimationDuration:1.1f];
[animationViewNormal setAnimationRepeatCount:0]; // 0=infinite loop
...
}
We load the stack of images into the mutable array then set the properties of our imageView with the image stack, duration and repeat count.
Next in the viewDidAppear() we start the image view animating:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
...
[animationViewNormal startAnimating];
...
}
Once the imageView is animating as an infinite loop we need to handle when leaving the view in the viewWillDisappear():
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
...
[animationViewNormal stopAnimating];
...
}
Last (which should be the second thing we add the .m file) we cleanup in the mutable array in the dealloc():
- (void)dealloc {
...
[animationViewNormalImages release];
[super dealloc];
}
This is how we handle it and works for us, but then again, we're normally not loading 4MB of images into memory to animate.
The .PNG files are compressed when building the app and I am not sure if they are decompressed on the fly when loading the images our of the resource bundle. This is a boolean value int he Build Properties Build Settings (COMPRESS_PNG_FILES).
For performance you may want to consider the following:
Mark opaque views as such:
Compositing a view whose contents are
opaque requires much less effort than
compositing one that is partially
transparent. To make a view opaque,
the contents of the view must not
contain any transparency and the
opaque property of the view must be
set to YES.
Remove alpha channels from opaque PNG
files: If every pixel of a PNG image
is opaque, removing the alpha
channel avoids the need to blend the
layers containing that image. This
simplifies compositing of the image
considerably and improves drawing
performance.
Furthermore, you may find it's better the make one large image with all 24 frames (offset by the width of the individual frame) and then load it once. Then using Core Graphics with CGContextClipToRect then just offset the image context. This means more code but may be faster than using the standard stack method.
Lastly, you may want to consider is converting the .PNG files into .PVR (PVRTC) files. More information can be found here: Apple Tech QA, Apple Docs, and Sample Code.
I hope this helps and please vote it up if it does.
Best,
Kevin
Able Pear Software
imageWithContentsOfFile: tends to take a long time to process, especially if there are lots of files (25 is kind of a lot) and/or they're big.
One thing you can try is to switch it out for imageNamed:, i.e.
[UIImage imageNamed:[NSString stringWithFormat:#"normal_%03d_crop.png", i]]
imageNamed: is generally much faster, but tends to cache images more or less indefinitely.
If loading the images into memory and keeping them around throughout the whole app is unacceptable, you may need to do some tweaky things to load them in at an appropriate time and to unload them after they've been used. That stuff is always tricky, and requires multithreading to not block the main UI while loading. But doable. And there are examples.
Load those images before using the start animating method . I advice an easier way " Just call start animating method in the applicationDidEnterForeground . When calling this one don't forget your UIImageView's alpha property . If you set uiimageiew.alpha=0.01; like this , your start animating method will be called and the user can't see this animation " so there will be no lag anymore.
I am developing an game in which i have to load images on finger swipe. For this i loaded all image 22 of size (788x525) at loading of the view itself. Then on finger swipe i just added image on view.
This works fine on iTouch but on iPhone game crashes after showing near about 12 images.
This is how i added images, graphicsArray is NSMUTableArray.
for ( int i=0; i<=totalNoofPages;i++){
NSString * graphicsFileName = #"Page";
graphicsFileName =
[graphicsFileName
stringByAppendingString:[NSString stringWithFormat:#"%d", i]];
UIImage *image =
[[UIImage alloc] initWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:graphicsFileName
ofType:#"jpg"]];
GraphicsImages =
[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 480,320 )];
GraphicsImages.image = image;
[image release];
[GraphicsImages setCenter:center];
GraphicsImages.transform = CGAffineTransformMakeRotation(M_PI/2);
[graphicsArray insertObject:GraphicsImages atIndex:i];
}
and on finger swipe i write,pageNo is number of page that will be displayed.
GraphicsImages = [graphicsArray objectAtIndex:pageNo];
[self addSubview:GraphicsImages];
Is there any way to release the previous loaded image from sub view as it might be kept on stack or is it due to large image size????
Please help me in this situation.
Thanks in advance.
Regards,
Vishal.
Are you crashing due to lack of memory?
You might try pre-loading the previous, current & next images only, and release them as soon as possible. Loading 22 images in memory takes up quite a lot of space.
Can you provide the error that you're getting when the app crashes? Knowing what exception is being triggered will help a lot in figuring out exactly what's going on.
I can tell you that you aren't releasing the GraphicsImages variable in your for loop, so that's causing memory leaks. I imagine that that's not enough to cause a crash, however. In any case, do this:
[ graphicsArray insertObject:GraphicsImages ];
[ GraphicsImages release ];
Note that you don't need to specify the insertion index in your case because the items will be added sequentially. Also, when you insert an object into an array, it is retained, so you can safely release it afterwards.