I am fairly new to iOS development. I have the following code
#property (nonatomic, strong) NSMutableArray *myImages;
-(void) viewDidLoad
{
[super viewDidLoad];
self.myImages = [[NSMutableArray alloc] init];
UIImageView *image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image1"]];
[self.myImages addObject:image];
[image setCenter:CGPointMake(10, 10)];
[self.view addSubview:image];
}
-(void) viewDidAppear:(BOOL)animated
{
for (;;) {
for (int i=0; i<[self.myImages count]; i++) {
[self.myImages[i] setCenter:CGPointMake(300, 300)];
}
}
}
When I do this, the image shows up at point 10,10. However it doesn't get changed to 300,300. Can any one suggest what I am doing wrong.
If I do something like
[self.myImages[0] setCenter:CGPointMake(300, 300)];
before the infinite loop, that works fine. but in the infinite loop no luck.
This code is just a snippet and a lot of the code is missing, but you should be able to understand what I am getting at with this.
Thanks
You forgot to add the imageView to the array. Add
[self.myImages addObject:image];
... in viewDidLoad.
As a side note: It's not common to abbreviate identifiers in Objective-C. Use imageView instead of image. Code becomes much easier to read.
You probably don't want the infinite loop in viewDidAppear, too.
first i'm kinda new to ios too so i am not sure.
i think there are 2 options:
i am not sure if view did appear is the right method to change the image i would suggest view will appear. and if you really want the one you used maybe try dispatch_async in order to change the image on the main ui thread.
Maybe try for in loop? that why you will set itrator as UIimage and it will recognize the image. this is less likly to solve it but maybe...
I have an array of images that changes to a random image with an IBAction attached to a button when pressed. When run on a simulator it runs fine but on a device it seems to crash after memory warnings. It also lags when the button is pressed. I want it to run smoothly, and not crash. I believe this has something to do with my array not releasing each image. Here is my array inside my buttons code.
-(IBAction)buttonPressed:(id)sender;
{
int ptr = arc4random() % 132;
NSArray* images = [[NSArray alloc] initWithObjects:#"17",#"29",#"55",#"400",#"Alcohol",#"Arianny",#"Anderson",#"Approach",#"Arab",#"Asian",#"Attitude",#"Attraction",#"Beckinsale",#"Blueberry",#"Brain",#"Break",#"Breakups",#"Burns",#"Buttocks",#"Charity",#"Check",#"Chicago",#"Chocolate",#"Coco",#"Coffee",#"College",#"Commit",#"Common",#"Confident",#"Cook",#"Count",#"Country",#"Couples",#"Courtship",#"Criminal",#"Cruz",#"Date",#"Date14",#"Deed",#"Degree",#"Dropped",#"Dushku",#"Dworaczyk",#"Eating",#"Emotion",#"Exercise",#"Fwb",#"Fantasies",#"Fitness",#"Flings",#"Flirt",#"Foot",#"Forget",#"Friendship",#"Frowning",#"Hum",#"Impression",#"Hair",#"Happiness",#"Hazel",#"Headache",#"Instant",#"Interest",#"Internet",#"Jacobs",#"January",#"Jimena",#"Jolie",#"Kalia",#"Kardashian",#"Kiss",#"Kissing",#"Krupa",#"Larissa",#"Latino",#"Laughter",#"Lip",#"London",#"Love",#"Love2",#"Love3",#"Love4",#"Math",#"Maximus",#"Melany",#"Memory",#"Men",#"Milian",#"Miller",#"Millions",#"Mind",#"Monica",#"Muscle",#"Partner",#"Naps",#"Negativity",#"Novels",#"Oral",#"Ossa",#"Pain",#"Positions",#"Productive",#"Proximity",#"Read",#"Reputation",#"Second",#"Sensitive",#"Serious",#"Shaking",#"Sleep2",#"Smile",#"Smoke",#"Smoke2",#"Smokers",#"Sneeze",#"Socks",#"Sold",#"Spot",#"Stimuli",#"Stone",#"Survey",#"Swell",#"Tattoo",#"Teacher",#"Teeth",#"Vickers",#"Violence",#"Wallet",#"Weight",#"Windmills.png",#"White",#"Women",#"Yawn",nil];
[imageView setImage:[UIImage imageNamed:[images objectAtIndex:ptr]]];
ptr++;
NSLog(#"button pressed");
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.images=nil;
}
- (void)dealloc {
[images release];
[adView release];
[super dealloc];
}
As I see your code is not running with ARC so when you create images array it is not deleted from memory until you call release. write release after you don't need your array anymore:
int ptr = arc4random() % 132;
NSArray* images = [[NSArray alloc] initWithObjects:#"17",#"29"];
[imageView setImage:[UIImage imageNamed:[images objectAtIndex:ptr]]];
ptr++;
[images release];
First off, use ARC, if you can.
You have 2 things leaking memory: the image and the array of image names. Since the image names are constant, you only need to create this array once.
Create ivars for the image and for the image name array:
UIImage *_image;
NSArray *_imageNames; // init this in your viewDidLoad:
Then, in your button press handler:
-(IBAction)buttonPressed:(id)sender;
{
int ptr = arc4random() % 132;
[_image release];
_image = UIImage imageNamed:_images[ptr]];
[imageView setImage:_image];
ptr++;
NSLog(#"button pressed");
}
Finally, release _imageNames:
- (void)dealloc
{
[_imageNames release];
// release everything else.
}
Again, you should really consider switching to ARC. You'll be glad you did.
You actually have two problems here, both surrounding this line:
NSArray* images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
The first is that the NSArray* at the beginning of the line declares a new local variable, images. This is separate from the property self.images that you try to erase in -viewDidUnload and release in -dealloc. Removing the NSArray* from the line will fix this issue, storing the array into the self.images property as you seem to intend.
That gives you a line like this:
images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
The second problem is that you re-create the images array each time you go through this method. That means that, even if you fixed the first problem, you would still be throwing away the old array without releasing it each time you passed through the method, so you'd still be leaking these arrays. There are a bunch of ways you could fix this, but the easiest one is probably to simply test if you already have an array and only create it if you haven't:
if(!images) {
images = [[NSArray alloc] initWithObjects: ...strings omitted... ,nil];
}
(Since all instances of this class have an identical list of image names, you could instead store the array in a static variable so it'd be shared between them—perhaps initialized by calling dispatch_once—but this isn't likely to make a difference unless you have many instances of this view controller on screen at the same time.)
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.
allocations before running 'extra loop'
The code:
// loading items to the array, there are no memory warnings after this is completed. The first allocations screenshot is after this code and before extra loop code.
NSMutableArray *albumCovers = [[NSMutableArray alloc] init];
for (MPMediaQuery *query in queries) {
NSArray *allCollections = [query collections];
for (MPMediaItemCollection *collection in allCollections) {
MPMediaItemArtwork *value = [collection.representativeItem valueForProperty:MPMediaItemPropertyArtwork];
UIImage *image = [value imageWithSize:CGSizeMake(100, 100)];
if (image) {
[albumCovers addObject:image];
}
}
}
}
_mediaCollections = [NSArray arrayWithArray:artworkedCollections];
_albumCovers = [NSArray arrayWithArray:albumCovers];
}
And somewhere else:
// !!!!! extra loop - from here the memory starts to grow and never release
for (i=0; i< 800; i++) {
UIImage * coverImage = [_albumCovers objectAtIndex:indexPath.row];
[veryTemp setImage:coverImage]; // exactly this line adds to the memory. with this line commented, there is no problem.
}
allocations after running 'extra loop'
and to clarify, call stack with only-obj-c on and system libraries off (if i turn them on, the highest % is 0.9% per heaviest method)
I've made some research, and found at stackoverflow, that these VM:ImageIO_PNG_Data are usually comming from [UIImage imageNamed:], however as you can see i don't use this method, i'm just getting the reference from MPMediaItemCollection.
The problem was that UIImage usually keeps just a ref(CGImageRef), which is a small one. After displaying items, CGImageRef was 'injected' with information. As a result the table was growing all the time.
The simple but not the most beautiful solution was to use the code:
NSArray = #[obj1, obj2, obj3]; // where obj is custom NSObject and has a UIImage property
instead of:
NSArray = #[img1, img2, img3]; // where img is of UIImage type
Wrap in #autorelease pool?
Also, why are you setting the image for veryTemp (I assume it's a UIImageView) 800 times in [veryTemp setImage:coverImage]; ?
Finally:
[_albumCovers objectAtIndex:indexPath.row];
You're getting the image object at the exact same index (indexPath.row) in your loop. I am not quite sure what you're trying to achieve in your code?
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.