I have an application in which I have taken imageview as a background and in that imageview I am using 89 images to make an animation.Here's my code to do the animation
-(void)viewWillAppear:(BOOL)animated
{
NSString *cachesDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSLog(#"cachesDirectoryPath: %#", cachesDirectoryPath);
CPIAppDelegate *obj=(CPIAppDelegate *)[[UIApplication sharedApplication]delegate];
arrayOfImages=[[NSMutableArray alloc]init];
viewMenu.hidden = obj.buttonStatus;
for (int i=0; i<IMAGE_ANIMATION_COUNT; i++) {
// [UIImage imageWithContentsOfFile:[NSString stringWithFormat:#"%#/streamvideo351_frame%d.jpg",cachesDirectoryPath,i]];
[arrayOfImages addObject:[UIImage imageNamed:[NSString stringWithFormat:#"streamvideo351_frame%d.jpg",i]]];
}
BackGrdImage.animationImages=arrayOfImages;
BackGrdImage.animationDuration =5.0;
BackGrdImage.animationRepeatCount =3;
[BackGrdImage startAnimating];
[arrayOfImages removeAllObjects];
}
and in dealloc method I am using
[imageAnimations release];
[BackGrdImage removeFromSuperview];
[BackGrdImage release];
it works fine on simulator But crashes on Ipad.What actually happens in ipad is sometimes it gets blink and some time time it disappear.So please help me out with this friends.I am also releasing the array on -(void)viewWillDisappear So please friends help me out with it Any help or suggestion will be appreciated.
Your app crashing because BackGrdImage animating only one time after that you releasing the array
[arrayOfImages removeAllObjects];
and
BackGrdImage.animationRepeatCount =3;
due to this your image goes animating second time but BackGrdImage does not get arrayOfImages for animation therefore your app going to crash.
There is one way when your image animation three time means in 1.5 sec then after you have to call one method for releasing your arrayOfImages.
The above process sure will work out.
May be you are getting memory warning. Because I don't think that device will support images array of 89 images/imageviews. Try debuggig your app on device.
Related
I'm using a UIImage animation and it is causing numerous memory leaks and crashes for different users using the application.
Below is my code. I am preloading the set of two animation in viewDidAppear
pointsView.image = [UIImage imageNamed:#"C72.png"];
NSMutableArray *menuanimationImages = [[NSMutableArray alloc] initWithCapacity:21];
NSString *imageName;
for( int aniCount = 0; aniCount < 72; aniCount++ )
{
imageName = [NSString stringWithFormat:#"C%d.png", aniCount];
[menuanimationImages addObject:[UIImage imageNamed:imageName]];
}
pointsView.animationImages = menuanimationImages;
pointsView2.image = [UIImage imageNamed:#"I72.png"];
NSMutableArray *menuanimationImagess = [[NSMutableArray alloc] initWithCapacity:21];
NSString *imageNames;
for( int aniCounts = 0; aniCounts < 72; aniCounts++ )
{
imageNames = [NSString stringWithFormat:#"I%d.png", aniCounts];
[menuanimationImagess addObject:[UIImage imageNamed:imageNames]];
}
pointsView2.animationImages = menuanimationImagess;
}
I am then running the animation using
pointsView.animationDuration = 3.11;
pointsView.animationRepeatCount = 1;
[pointsView startAnimating];
Any suggestions?
Please read my blog post about this subject: video-and-memory-usage-on-ios-devices. The root of the problem is that you simply cannot have this many images loaded into main memory at the same time. You need to simply not use the UIImageView.animationImages API, it is badly broken and lures developers into writing bad code that will crash when run on the device.
You are loading it looks like 72 png images into memory at once? And depending on the size of those images, you could probably be reaching the memory limits of some older devices causing them to give a memory warning and eventually crash. My suggestion is to not do a 72 image animation. You could try to compress each image which will lower their quality and memory size but loading 72 images to do an animation is just not good in the first place.
I have a strange issue .
I am currently working on a mail app and it used to crash randomly without and error or logs in my console . I checked my Crash Log it showed me Low Memory warning with jettisoned written next to my App.
So I suspect it's an memory issue and went back to trace my memory usage of my application.
I used allocation instrument to detect the overall usage and my application crashed when it's heap size was just 4.59 MB.
Instruments point towards a function where I am using MBProgressHUD indicator.
The culprit is this one line :
[appDelegate showHUDActivityIndicatorOnView:self.parentViewController.view whileExecuting:#selector(refreshInBackground) onTarget:self withObject:nil withText:#"Loading"];
If I replace this with
[self refreshInBackground] everything works fine no issues ..
Here is the code :
-(void) showHUDActivityIndicatorOnView:(UIView*)view whileExecuting:(SEL)method
onTarget:(id)target withObject:(id)object withText:(NSString*)label{
self.HUD = [[MBProgressHUD alloc] initWithView:view] ;
self.navigationController.navigationItem.hidesBackButton = YES;
[view addSubview:self.HUD];
self.HUD.delegate = nil;
self.HUD.labelText = label;
[self.HUD showWhileExecuting:method onTarget:target withObject:object animated:YES];
}
self.HUD is a property which is being retained.
With a slight modification is showWhileExecuting method as follows :
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated {
MyAppdelegate *appDelegate = (MyAppdelegate *)[UIApplication sharedApplication].delegate;
if(!appDelegate.isRequestActive)
{
methodForExecution = method;
targetForExecution = [target retain];
objectForExecution = [object retain];
// Launch execution in new thread
taskInProgress = YES;
[NSThread detachNewThreadSelector:#selector(launchExecution) toTarget:self withObject:nil];
// Show HUD view
[self show:animated];
}
else {
[self done];
}
}
currently I have removed this from my app and it works fine now and it does not crashes even with the usage of 20 - 30 MB heap Memory .
I am not looking for an specific answer or solution here . What I am looking for ways to debug / techniques to debug the issue so that I can get to know what caused my app to crash .
Is it memory overflow . If that's the case then how can I use 20-30 MB right now.
If it's not an memory issue why does my crash reporter shows jettisoned next to my App name .
the culprit line ([appDelegate showHUDActivityIndicatorOnView:self.parentViewController.view whileExecuting:#selector(refreshInBackground) onTarget:self withObject:nil withText:#"Loading"])
Every time when I call this function some memory increases , because of some elements being cached . But when memory reached 4.5 MB ...this line cause it to crash
How do I get to the root cause of this issue . How do I figure out why iOS is killing my app the reason for jettisoned written next to my app
Any help or suggestions would be really appreciated .
Ok. The problem is I was adding the HUD progress bar on my view using
[view addSubview:self.HUD];
I forgot to remove it from the super view in its delegate method:
- (void)hudWasHidden:(MBProgressHUD *)hud
{
// Remove HUD from screen when the HUD was hidded
[HUD removeFromSuperview]; // app crashes at 4.59 MB if you comment this
[HUD release];
HUD = nil;
}
Because of this several views were added every time on one UIView ... I guess there is an upper limit for the number of child subviews on top of each UIView .... apple should mention this in their documentation ...
If you're not using ARC, you have for sure a leak every time you assign the property:
self.HUD = [[MBProgressHUD alloc] initWithView:view] ;
Change your code in this way:
MBProgressHUD *progressHUD = [[MBProgressHUD alloc] initWithView:view] ;
self.HUD = progressHUD;
[progressHUD release];
I want to save an image into albums.
I am doing it using UIImageWriteToSavedPhotosAlbum API.
Its working perfectly fine on Simulator, but crashes on Device giving EXC_BAD_EXCESS.
I have enables NSZombi.. argument, but it shows no log of any deallocated memory reused.
- (IBAction)saveImageOnHDButtonPressed:(id)sender{
NSArray *subviews = [scroll subviews];
UIImage *img = [[subviews objectAtIndex:0] image];
UIImageWriteToSavedPhotosAlbum(img, self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
}
Please help..
Thanks
Try retaining the image before you save it.
In my view controller, how can I know when a certain UIImageView has finished loading (large jpeg from documents directory)? I need to know so that I can then swap a placeholder low-res imageview with this hi-res imageview. Do I need to create a custom callback to know this? Any way is fine.
By the way, here is a snippet of code where I load the image:
NSString *fileName = [NSString stringWithFormat:#"hires_%i.jpg", currentPage];
NSString *filePath = [NSString stringWithFormat:#"%#/BookImage/%#", [self documentsDirectory], fileName];
hiResImageView.image = [[[UIImage alloc] initWithContentsOfFile:filePath] autorelease];
UIImageView isn't doing any loading at all. All the loading is being done by [[UIImage alloc] initWithContentsOfFile:filePath], and your thread is blocked while the file is loaded (so the load is already complete by the time that call finally returns).
What you want to do is something like this:
- (void)loadImage:(NSString *)filePath {
[self performSelectorInBackground:#selector(loadImageInBackground:) withObject:filePath];
}
- (void)loadImageInBackground:(NSString *)filePath {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath];
[self performSelectorOnMainThread:#selector(didLoadImageInBackground:) withObject:image waitUntilDone:YES];
[image release];
[pool release];
}
- (void)didLoadImageInBackground:(UIImage *)image {
self.imageView.image = image;
}
You would set up self.imageView to display the low-res image and then call loadImage: to load the high-res version.
Note that if you call this repeatedly before didLoadImageInBackground: gets called from earlier calls, you may cause the device to run out of memory. Or you might have the image from the first call take so much longer to load than image from the second call that didLoadImageInBackground: gets called for the second image before it gets called for the first. Fixing those issues is left as an exercise for the reader (or for another question).
I have a UIScrollView that has a set of images loaded side-by-side inside it. You can see an example of my app here: http://www.42restaurants.com. My problem comes in with memory usage. I want to lazy load the images as they are about to appear on the screen and unload images that aren't on screen. As you can see in the code I work out at a minimum which image I need to load and then assign the loading portion to an NSOperation and place it on an NSOperationQueue. Everything works great apart from a jerky scrolling experience.
I don't know if anyone has any ideas as to how I can make this even more optimized, so that the loading time of each image is minimized or so that the scrolling is less jerky.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
[self manageThumbs];
}
- (void) manageThumbs{
int centerIndex = [self centerThumbIndex];
if(lastCenterIndex == centerIndex){
return;
}
if(centerIndex >= totalThumbs){
return;
}
NSRange unloadRange;
NSRange loadRange;
int totalChange = lastCenterIndex - centerIndex;
if(totalChange > 0){ //scrolling backwards
loadRange.length = fabsf(totalChange);
loadRange.location = centerIndex - 5;
unloadRange.length = fabsf(totalChange);
unloadRange.location = centerIndex + 6;
}else if(totalChange < 0){ //scrolling forwards
unloadRange.length = fabsf(totalChange);
unloadRange.location = centerIndex - 6;
loadRange.length = fabsf(totalChange);
loadRange.location = centerIndex + 5;
}
[self unloadImages:unloadRange];
[self loadImages:loadRange];
lastCenterIndex = centerIndex;
return;
}
- (void) unloadImages:(NSRange)range{
UIScrollView *scrollView = (UIScrollView *)[[self.view subviews] objectAtIndex:0];
for(int i = 0; i < range.length && range.location + i < [scrollView.subviews count]; i++){
UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)];
if(subview != nil && [subview isKindOfClass:[ThumbnailView class]]){
ThumbnailView *thumbView = (ThumbnailView *)subview;
if(thumbView.loaded){
UnloadImageOperation *unloadOperation = [[UnloadImageOperation alloc] initWithOperableImage:thumbView];
[queue addOperation:unloadOperation];
[unloadOperation release];
}
}
}
}
- (void) loadImages:(NSRange)range{
UIScrollView *scrollView = (UIScrollView *)[[self.view subviews] objectAtIndex:0];
for(int i = 0; i < range.length && range.location + i < [scrollView.subviews count]; i++){
UIView *subview = [scrollView.subviews objectAtIndex:(range.location + i)];
if(subview != nil && [subview isKindOfClass:[ThumbnailView class]]){
ThumbnailView *thumbView = (ThumbnailView *)subview;
if(!thumbView.loaded){
LoadImageOperation *loadOperation = [[LoadImageOperation alloc] initWithOperableImage:thumbView];
[queue addOperation:loadOperation];
[loadOperation release];
}
}
}
}
EDIT:
Thanks for the really great responses. Here is my NSOperation code and ThumbnailView code. I tried a couple of things over the weekend but I only managed to improve performance by suspending the operation queue during scrolling and resuming it when scrolling is finished.
Here are my code snippets:
//In the init method
queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:4];
//In the thumbnail view the loadImage and unloadImage methods
- (void) loadImage{
if(!loaded){
NSString *filename = [NSString stringWithFormat:#"%03d-cover-front", recipe.identifier, recipe.identifier];
NSString *directory = [NSString stringWithFormat:#"RestaurantContent/%03d", recipe.identifier];
NSString *path = [[NSBundle mainBundle] pathForResource:filename ofType:#"png" inDirectory:directory];
UIImage *image = [UIImage imageWithContentsOfFile:path];
imageView = [[ImageView alloc] initWithImage:image andFrame:CGRectMake(0.0f, 0.0f, 176.0f, 262.0f)];
[self addSubview:imageView];
[self sendSubviewToBack:imageView];
[imageView release];
loaded = YES;
}
}
- (void) unloadImage{
if(loaded){
[imageView removeFromSuperview];
imageView = nil;
loaded = NO;
}
}
Then my load and unload operations:
- (id) initWithOperableImage:(id<OperableImage>) anOperableImage{
self = [super init];
if (self != nil) {
self.image = anOperableImage;
}
return self;
}
//This is the main method in the load image operation
- (void)main {
[image loadImage];
}
//This is the main method in the unload image operation
- (void)main {
[image unloadImage];
}
I'm a little puzzled by the "jerky" scrolling. Since NSOperationQueue runs operations on separate thread(s) I'd have expected at worst you might see empty UIImageViews showing up on the screen.
First and foremost I'd be looking for things that are impacting the processor significantly as NSOperation alone should not interfere with the main thread. Secondly I'd be looking for details surrounding the NSOperation setup and execution that might be causing locking and syncing issues which could interrupt the main thread and therefore impact scrolling.
A few items to consider:
Try loading your ThumbnailView's with a single image at the start and disabling the NSOperation queuing (just skip everything following the "if loaded" check. This will give you an immediate idea whether the NSOperation code is impacting performance.
Keep in mind that -scrollViewDidScroll: can occur many times during the course of a single scroll action. Depending on how for the scroll moves and how your -centerThumbIndex is implemented you might be attempting to queue the same actions multiple times. If you've accounted for this in your -initWithOperableImage or -loaded then its possible you code here is causing sync/lock issues (see 3 below). You should track whether an NSOperation has been initiated using an "atomic" property on the ThumbnailView instance. Prevent queuing another operation if that property is set and only unset that property (along with loaded) at the end of the NSOperation processes.
Since NSOperationQueue operates in its own thread(s) make sure that none of your code executing within the NSOperation is syncing or locking to the main thread. This would eliminate all of the advantages of using the NSOperationQueue.
Make sure your "unload" operation has a lower priority than your "load" operation, since the priority is the user experience first, memory conservation second.
Make sure you keep enough thumbnails for at least a page or two forward and back so that if NSOperationQueue falls behind, you have a high margin of error before blank thumbnails become visible.
Make sure your load operation is only loading a "pre-scaled" thumbnail and not loading a full size image and rescaling or processing. This would be a lot of extra overhead in the middle of a scrolling action. Go even further and make sure you've converted them to PNG16 without an alpha channel. This will give at least a (4:1) reduction in size with hopefully no detectable change in the visual image. Also consider using PVRTC format images which will take the size down even further (8:1 reduction). This will greatly reduced the time it takes to read the images from "disk".
I apologize if any of this doesn't make sense. I don't see any issues with the code you've posted and problems are more likely to be occurring in your NSOperation or ThumbnailView class implementations. Without reviewing that code, I may not be describing the conditions effectively.
I would recommend posting your NSOperation code for loading and unloading and at least enough of the ThumbnailView to understand how it interacts with the NSOperation instances.
Hope this helped to some degree,
Barney
One option, although less visually pleasing, is to only load images when the scrolling stops.
Set a flag to disable image loading in:
-scrollViewWillBeginDragging:
Re-enable loading images when the scrolling stops using the:
-scrollViewDidEndDragging:willDecelerate:
UIScrollViewDelegate method. When the willDecelerate: parameter is NO, the movement has stopped.
the problem is here:
UIImage *image = [UIImage imageWithContentsOfFile:path];
It seems that threaded or not when you load a file from disk (which maybe that happens on the main thread regardless, I'm not totally sure) everything stalls. You normally don't see this in other situations because you don't have such a large area moving if any at all.
While researching this problem, I found two more resources that may be of interest:
Check out the iPhone sample project "PageControl": http://developer.apple.com/iphone/library/samplecode/PageControl/index.html
It lazy loads view controllers in a UIScrollView.
and -
Check out the cocoa touch lib: http://github.com/facebook/three20 which has a 'TTPhotoViewController' class that lazy loads photos/thumbnails from web/disk.
Set shouldRasterize = YES for the sub content view adde to the scrollview. It is seen to remove the jerky behavior of custom created scroll view like a charm. :)
Also do some profiling using the instruments in the Xcode. Go through the tutorials created for profiling by Ray Wenderlich it helped me a lot.