Animation Using To Much Memory iOS7 - ios

I have a series of .png images loaded into an animation, which works fine, but when I switch to another view, the memory that the animation uses is not freed up. Which takes up more and more memory every time you return to that view until eventually the app quits due to too much memory pressure.
Is there anything I can put in view diddisappear or another method to free this memory from the animation when the view is changed?
Also this only seems to happen in with my 4S running iOs 7. On my 4S with 6.1.2 it runs smoothly.
NSArray *animationArray = [NSArray arrayWithObjects:
[UIImage imageNamed:#"0001.png"],
[UIImage imageNamed:#"0002.png"],
[UIImage imageNamed:#"0003.png"],
...
nil];
self->tapanimation1.animationImages =animationArray;
self->tapanimation1.animationDuration = .5;
self->tapanimation1.animationRepeatCount = 1;
[self->tapanimation1 startAnimating];

How you alloc image in animation --- there are two ways of intializing UIImage
First is -->
[UIImage imageNamed:#"kshitij.png"];
There is issue with this method it doesnot dealloc memory , even on release .
Second is -->
If image is in your app Bundle,then always use this
NSString *filePath = [[NSBundle mainBundle]pathForResource:#"kshitij" ofType:#"png"];
UIImage *image = [[UIImage alloc]initWithContentsOfFile:filePath];
Now use this image and make it release too , it will surely will save your some MB.
and you can also use ARC for memory saving.
Try this code --
NSSMutableArray *imageNameArray = [[NSSMutableArray
alloc]initWithObjects:#"0001",#"0002",nil];
NSMutableArray *imageArray = [[NSMutableArray alloc] init];
for(int i=0;i<imageNameArray.count;i++)
{
NSString *filePath = [[NSBundle mainBundle]pathForResource:[imageNameArray
objectAtIndex:i] ofType:#"png"];
UIImage *image = [[UIImage alloc]initWithContentsOfFile:filePath];
[imageArray addObject:image];
}
self->tapanimation1.animationImages = imageArray;
self->tapanimation1.animationDuration = .5;
self->tapanimation1.animationRepeatCount = 1;
[self->tapanimation1 startAnimating];
Now when animation does stop just release array. If you are using ARC then make it nil.

How are you navigating to your new view controller, and how are you getting back? Are you using an unwind segue? Are you presenting the new view controller as a modal and then dismissing it?
Or are you using a segue to link to the second VC, and then another segue to link back? If you are using a second (non-unwind) segue to link back, then that's your problem. That would cause you to create a new instance of your first view controller each time.
As for releasing memory, you could certainly set your view's animationImages array to inil in viewDidDisappear, and then re-load the images in viewWillAppear, but it doesn't make sense that you memory footprint would go up with each round trip to the second view controller. That indicates a problem.

Related

ImagePicker Camera only loads when in background thread - else it's a black screen

I have a tab-based app with 2 tabs: the first one performs multiple operations in a background thread (downloading json) and updates the UI on the main thread when the fetching is over. The second tab presents a camera as soon as it appears. When I open the app, the fetching starts in background in tab #1. If I switch to tab #2 while in the background thread in tab 1, the camera loads. If I wait until the main thread updated the UI (still tab 1) before switching to tab 2, the camera takes 10 seconds to load, only showing a black screen. What's even more weird is that the NSLogs tell me the camera is supposed to be already loaded, but a black screen shows up. My question is, is there a way to "clear" the main thread when tab #2 appears, or even better, is there a way to show the camera as a high priority task in the main thread?
This is the code in ViewDidAppear (Tab 2):
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"4");
[self showImagePickerForSourceType:UIImagePickerControllerSourceTypeCamera];
});
Next:
- (void)showImagePickerForSourceType:(UIImagePickerControllerSourceType)sourceType
{
NSLog(#"5");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"6");
[[UIApplication sharedApplication] setStatusBarHidden:YES];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{
NSLog(#"7");
imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.modalPresentationStyle = UIModalPresentationCurrentContext;
imagePickerController.sourceType = sourceType;
imagePickerController.delegate = self;
imagePickerController.showsCameraControls = NO;
// NSLog(#"HERE");
if (isiPhone5)
{
NSLog(#"8");
[[NSBundle mainBundle] loadNibNamed:#"OverlayView" owner:self options:nil];
}
else
{
NSLog(#"not 5");
[[NSBundle mainBundle] loadNibNamed:#"Over2" owner:self options:nil];
}
self.overlayView.frame = imagePickerController.cameraOverlayView.frame;
imagePickerController.cameraOverlayView = self.overlayView;
self.overlayView = nil;
self.imagePickerController = imagePickerController;
[self presentViewController:self.imagePickerController animated:NO completion:nil];
NSLog(#"9 DONE");
}
});
}
Seeing log output doesn't necessarily mean that your camera view is visible, hence why there is a completion block provided to you in the presentViewController:animated:completion: method. Have you tried putting some code there to see when that's run?
This is the kind of problem that the time profiler tool in instruments was designed for. If you haven't already, I highly suggest investigating with that tool, as it can basically pinpoint what's causing the lag in your app.
I posted an answer here (iOS 7 UIImagePicker preview black screen) that may be a temporary solution to your issue. Essentially the UIImagePickerController has a memory leak in iOS 7, meaning that it'll get slower and slower with each instantiation until you have 10+ seconds of black screen while you wait for the camera to load.
As mentioned in the link above, I recommend subclassing UIImagePickerController or just making it an instance variable in your ViewController so its ever only instantiated once.
I know you're looking for an answer more along the lines of "clearing the main queue", but hopefully this will help you out in the meantime.

iOS application behaviour in case of low memory

i have iPhone application. In some cases, when the device is getting low on free memory, some actions (for example, opening the camera) might cause the application to crash.
My question is that:
I want to prevent these crashes, what is the common way applications
do such thing (blocking specific actions, notifying the user, other
ideas)? I ask because i didn't encountered such behaviour in iOS
applications i ran into.
Are there any ways of preventing such crashes and remain full app functionality, such as iOS system calls to free more memory and etc.? if anyone has best practice or good heuristic i would love to hear about it.
EDIT: I ask this question assuming i already implement the 'didReceiveMemoryWarning' function and freed all the memory i can.
EDIT 2: my app is about pictures. A lot like camera scanner apps, this app allows taking pictures, image processing and saving data about them in memory. my crashes usually happens when i scan a lot of pictures.
Some thumb rules i follow:
Using Arc
Use weak for iboutlets (except top level example: UIwindow) and for delegates
Use Strong for class properties and copy for NSString.
Dont access variables directly, use self....way.
Dont use autorelease way of creating new objects, example NSArray *array = [NSArray arrayWithObjects......., instead use NSArray *array = [NSArray alloc] initWit....
Same way for NSString class. try to use [NSString alloc] initWithFormat..... instead of [NSString stringWithFormat.
When ever you are adding NSNotification(addObserver...) centre must remove(removeObserver..) them in dealloc.
Implement didReceiveMemoryWarning(view controller level) or applicationDidReceiveMemoryWarning(application level and it is called first than view controller level) properly, how ever there are times when you only and only wish to save from crash.you can display an alert telling user less memory available, you can pop/present ..user to home screen.(Bad practice).
Dont perform any manipulation on main thread while being in background thread.Always use #autorelease block for background threads.
use GCD/NSOperation queue for long running processes.
Keep an sharp eye on image resources you are using, use image only of desired size not scale big image to small image size for your need.
USE autorelease pool for long running loops, which create a lot of autoreleased objects.
i have some code snippet for you which ypu can follow:
//way 1 all on main thread bad approach, basically we are just doing some image manipulation on main thread(should not do on main thread :))
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication]delegate];
[appDelegate showLandscapeLoading];//think it as progress view/loader
UIImage *pickedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
NSData *imageData = UIImagePNGRepresentation(pickedImage);
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"category_imagename.jpeg"];
NSError * error = nil;
//from here
[imageData writeToFile:path options:NSDataWritingAtomic error:&error];
**//the important part for discussion UI manipulation on main thread bad bad bad**
CGSize size1;//A
size1.width = 400;
size1.height = 400;
UIGraphicsBeginImageContext(size1);
[pickedImage drawInRect:CGRectMake(0, 0, size1.width, size1.height)];
UIImage *bigImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSString *bigThumb = [documentsDirectory stringByAppendingPathComponent:#"category_thumb_imagename.jpeg"];
NSData *data1=UIImageJPEGRepresentation(bigImage, 0.5);
BOOL status1=[data1 writeToFile:bigThumb atomically:YES];
**//up to here should be in non ui thread/seperate thread**
**//below code should go in main thread**
NSLog(#"status1 -> %d",status1);
[self setCategoryImageName:bigImage];
[self.imgCategory setImage:pickedImage];
if (status1) {
isAddingCategoryImage = YES;
}
[appDelegate stopLandscapeLoading];
if (error != nil) {
NSLog(#"Error: %#", error);
return;
}
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:true];
}
[picker.view removeFromSuperview];
}
The correct way:
Using NSOperation:
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
YourApplicationDelegate *appDelegate = (YourApplicationDelegate *)[[UIApplication sharedApplication]delegate];
[appDelegate showLandscapeLoading];
UIImage *pickedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSError * error = nil;
NSOperationQueue *opQueue = [[NSOperationQueue alloc] init];
[opQueue addOperationWithBlock:^
{
// Create a graphics image context very slow stuff
CGSize newSize = CGSizeMake(400, 400);
UIGraphicsBeginImageContext(newSize);
// Tell the old image to draw in this new context, with the desired
// new size
[pickedImage drawInRect:CGRectMake(0,0,newSize.width,newSize.height)];
// Get the new image from the context
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// End the context
UIGraphicsEndImageContext();
NSString *bigThumb = [documentsDirectory stringByAppendingPathComponent:#"category_thumb_imagename.jpeg"];
NSData *data1=UIImageJPEGRepresentation(newImage, 0.5);
BOOL status1=[data1 writeToFile:bigThumb atomically:YES];
// ok, now do UI stuff in the main queue
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
[self setCategoryImageName:bigThumb];
[self.imgCategory setImage:pickedImage];
if (status1) {
isAddingCategoryImage = YES;
}
[appDelegate stopLandscapeLoading];
if (error != nil) {
NSLog(#"Error: %#", error);
return;
}
if ([self.popoverController isPopoverVisible]) {
[self.popoverController dismissPopoverAnimated:true];
}
[picker.view removeFromSuperview];
}];
}];
}
thanks and regards,
ALOK
If you use non arc and You had allocated the many object and you did not release these object so it shows the memory problem. you relase all object in dealloc method.In goes upper product option and choose the Analyze. you will see where your application memory leak
If you have used old xcode and you have use new iphone simulator than it shows the memory leak
If you use arc than please comment the autorelease or [obj release] close.
Further than if you want to check their application than side corner button to hold and choose profile. it will show instruments tools. you can enable Nszombies. than you can check how to object values have take and you can see where the memory leak in your application.
No there are is no direct call to free RAM memory in iOS. If you use ARC in your project, define your properties as weak/strong etc correctly and have checked your application for memory leaks or zombie processes there will not be a RAM issue.
iOS frees up memory from other apps to allocate it it to the foreground app if needed and you should not try to deal with it. If you app crashes due to memory issues, you probably have a memory leak in your application. Use Instruments to profile your app.
The memory warning system had a lot of improvements since I started to develop for iOS, ARC also works great helping developers managing memory.
You should profile your app using leaks and allocations to see why your app is consuming so much memory.
Which kind of application are you developing? should be a high memory usage application such as games, or photos app?
A crash could be due to a not well managed answer to a memory warning or to a huge memory occupation that doesn't leave any last breath to your app.
The most common reason are pictures. These devices can't handle a lot of hires resources if you don't manage those situations in the right way, the memory footprint of your app grows until it can't free enough memory.
You need to give more details, though.

iOS 7, UIView subviews is nil

Why in iOS 7 [UIView subviews] is always return a nil ?
I am using this code, but already get a nil.
Bug in iOS 7 ?
EDITED:
TWTweetComposeViewController* tweetSheet = [[TWTweetComposeViewController alloc] init];
[tweetSheet setInitialText: twitter_descrp];
[tweetSheet addURL:[NSURL URLWithString: twitter_link]];
NSData* imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString: twitter_img]];
UIImage* uiImage = [UIImage imageWithData:imageData];
[tweetSheet addImage:uiImage];
NSLog(#"subviews: %#", [tweetSheet.view firstSubviewOfClass:[UIButton class]] );
Here firstSubviewOfClass is got from iOS7 Subviews Hierarchy
And inside a NSLog is return this:
subviews: (
)
iOS 7 hierarchy changed a lot. As one example, Apple replaced many UIButtons by table views and table view cells.
Are you sure there is actually a button in the hierarchy? For debugging, you could try to call
NSLog(#"%#", [tweetSheet.view recursiveDescription]); // (ignore the warning)
to see how the view hierarchy looks like.
However, in iOS 7 Apple has started to intentionally block access to system views (UIAlertView for an example). Sometimes because of security, sometimes because for their own reasons. That means it is possible you won't be able to access the button at all.
You shouldn't be accessing the button.

Displaying a PDF with UIWebView Not Working

So, I realize that there have been many questions regarding using a UIWebView to display a PDF in an app (on the iPad). I have reviewed everything I can find but I can't seem to find any satisfaction.
What I'm trying to do is very basic so I really don't know why it's not working. All I need to do is display a locally stored PDF in the UIWebView. Essentially, all I'm getting is a black screen. If someone could take a look at my code, I'd really appreciate it. Thanks.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Segue to the materials screen.
if ([segue.identifier isEqualToString:#"materials"]) {
PDFViewController *pdfViewController = [segue destinationViewController];
NSIndexPath *path = [self.tableView indexPathForSelectedRow];
int row = [path row];
Lesson *selected = [purchasedLessons objectAtIndex:row];
pdfViewController.selectedLesson = selected ;
//Start Load PDF in UIWebView
pdfViewController.pdfWindowTitle.title = selected.titleAndSubtitle;
pdfViewController.pdfWebView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 1024, 704)];
NSString *urlAddress = [[NSBundle mainBundle] pathForResource:#"moonlightSonataFirstMovement" ofType:#"pdf"];
NSURL *url = [NSURL fileURLWithPath:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[pdfViewController.pdfWebView loadRequest:requestObj];
//End Load PDF
}
}
I've checked to see if my object is bring passed into the scene properly (and it is) and if I am getting a proper request out; i'm getting:
<NSURLRequest file://localhost/Users/MYCOMPUTER/Library/Application%20Support/iPhone%20Simulator/5.0/Applications/AB161E57-D942-44C2-AA75-030087820BED/iLessons%20Piano.app/moonlightSonataFirstMovement.pdf>
Also, I've have this error message:
iLessons Piano[23080:f803] DiskImageCache: Could not resolve the absolute path of the old directory.
Additionally, the NSLog message gets printed out 8 times for some reason.
The only thing I can think of is that I need to do this loading when I call my prepareForSegue function in the previous scene. That or use a different method like Quartz. But I'd rather use a UIWebView since all I really need to do is display and allow scrolling.
DiskImageCache: Could not resolve the absolute path of the old
directory.
This one isn't the real reason the app crashes. This warning can be fixed by assigning the view in the Storyboard. It seems like it's connected already but it's grey. So assign it again and it will be fine.
The real issue for me was that the PDF images were 300 DPI and it took too long to load the application. Since the debugger keeps the app from crashing it seems to work fine but the loading will take too long without the debugger and will result in a timeout crash.
There's a few things you can do. You can downscale your PDF which might be a good thing anyway since older device are even slower and it's nice to support those as well. But what really fixes it is by delaying the PageView initialization. Here's what I did:
In the RootViewController I moved the code from the viewDidLoad to a new function setupPageViewer.
And put this in the viewDidLoad:
[self performSelector:#selector(setupPageViewer) withObject:nil afterDelay:0.0f];
The delay 0.0 means it will be taken care of in the next frame which gives you the opportunity to show a loading indicator.
Enjoy!
im not sure about whats going on .. you view and webView both holding nil value.. as i told you im not involved in the storyBoard yet .. but you as a workaround solution maybe this fix your problem
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 1027, 768)];
pdfViewController.view = view;
pdfViewController.pdfWebView = [[UIWebView alloc] initWithFrame:pdfViewController.view.frame];
NSString *urlAddress = [[NSBundle mainBundle] pathForResource:#"moonlightSonataFirstMovement" ofType:#"pdf"];
NSURL *url = [NSURL fileURLWithPath:urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[pdfViewController.view addSubview:pdfViewController.pdfWebView];
[pdfViewController.pdfWebView loadRequest:requestObj];
As a workaround, you can disable or remove your 'All Exceptions' breakpoint. This might make debugging a little more difficult, but it's not as bad as having to relaunch the application all the time.
This is the breakpoint causing the issue. I had set it so long ago that I'd forgotten it was there

UIImageView animation crashing (sigabrt)

Any idea why this crashes? What am I doing wrong?
Thanks!
-(IBAction)animationOneStart {
NSMutableArray* arrayOfImages = [[NSMutableArray alloc]initWithCapacity:10];
for(int count = 1; count <= 22; count++)
{
NSString *path = [NSString stringWithFormat:#"testDance3_%03d.jpg", count];
UIImage* img = [[UIImage alloc]initWithContentsOfFile:path];
[arrayOfImages addObject:img];
[img release];
}
loadingImageView.animationImages = arrayOfImages;
[arrayOfImages release];
loadingImageView.animationDuration = 3;
loadingImageView.animationRepeatCount = 1; //Repeats indefinitely
[loadingImageView startAnimating];
loadingImageView.animationImages = nil;
[loadingImageView release];
}
Try initializing your NSMutableDictionary with capacity for the same quantity of object's you'll be storing in it. If that doesn't work, I'd try to comment out these two lines:
//loadingImageView.animationImages = nil;
//[loadingImageView release];
In the scope of this code, your other calls appear balanced. But we can't see what's happening inside loadingImageView, and so my guess is that either the loadingImageView itself or it's animations are being released prematurely.
I can't see enough code confirm this, but a couple things that are suspicious are here:
[loadingImageView startAnimating];
loadingImageView.animationImages = nil;
[loadingImageView release];
So, while the animation is running, you are releasing the images that are being animated? Or the view which, itself, is animating? Probably one or the other or both is the problem.
If the animation is supposed to run indefinitely, you are going to need to keep the view around indefinitely. And if it stops eventually, you should release it after it stops.
You call to addObject will fail when nil is passed. The UIImage could be nil when your system run out of memory and cannot allocate more. When that happens depends on how large your images are, but basically you can be sure that sooner of later use of loadingImageView.animationImages combined with allocating all your UIImage objects at the same time will cause your app to crash. See also playing-looping-images-sequence-in-uiview. You basically need a different approach that does not hold all the images in memory at the same time, the uncompressed images consume way too much memory.

Resources