NSArray received memory warning - ios

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.)

Related

Objective C setCenter on Image

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...

MPMediaItemPropertyArtwork causes crash (weird issue)

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?

updating UIImageView creates memory leak

I have researched hundreds of posts, but still cannot figure out where my problem is. I have an array of image names and I'm trying to randomly select an image and update the imageview on an event. When run on my device it crashes after 12-15 images.
- (void)viewDidLoad
{
[super viewDidLoad];
self.predictionArray = [[NSArray alloc] initWithObjects:#"IMG_0006.JPG",
#"IMG_0007.JPG",
#"IMG_0008.JPG",
#"IMG_0034.jpg",
#"IMG_0036.jpg",
#"IMG_0043.jpg",
#"IMG_0062.JPG",
#"IMG_0069.JPG",
#"IMG_0076.jpg",
#"IMG_0093.jpg",
#"IMG_0096.jpg",
#"IMG_0168.jpg",
#"IMG_0240.jpg",
#"IMG_0251.jpg",
#"IMG_0262.jpg",
#"IMG_0264.jpg",
#"IMG_0310.jpg",
#"IMG_0351.jpg",
#"IMG_0355.jpg",
#"IMG_0391.jpg",
#"IMG_0404.jpg",
#"IMG_0417.jpg",
#"IMG_0428.jpg",
#"IMG_0461.jpg",
#"IMG_0471.jpg",
#"IMG_0485.jpg",
#"IMG_0492.jpg",
#"IMG_0550.jpg",
#"IMG_0568.jpg",
#"IMG_0822.jpg", nil];
[self makePrediction];
}
- (void) makePrediction {
NSUInteger index = arc4random_uniform(self.predictionArray.count);
[self.pageImage setImage:[UIImage imageNamed:[self.predictionArray objectAtIndex:index ]]];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self makePrediction];
}
You are using the imageNamed: method to set the image of the imageview.But problem is imageNamed: method who has its own caching mechanism that you dont have any control over it.So when & where the allocated memory is released we don't know.So instead of using imageNamed: method use the following method to set the image.
NSString* imgpath= [ [ NSBundle mainBundle] pathForResource:#"sample" ofType:#"png"];
imgviw.image = [ UIImage imageWithContentsOfFile: imgPath];
The main advantage of using the imageWithContentsOfFile: method is this method does NOT cache the image, and therefore does not cause any memory issues with retaining large images. Also before applying image to imageview you can set the imgviw.image = nil & then set the image to avoid the memory leaks problems

iOS - XCode 4.4 - Potential Memory Leaks Using Analyze

I ran analyze on my 1st iPhone app, and I see a few potential memory leaks. The app itself works fine on the simulator.
I would like to do the right thing and clear the potential memory leaks, but some are quite puzzling. Maybe someone could help me here?
Thanks in advance.
Pier.
Error 1) The Analyzer says "Potential leak of an object stored in tempDate and tempAns"
#import "Answer.h"
#implementation Answer
#synthesize answerTiming;
#synthesize xPosition;
#synthesize earlyOrLate;
#synthesize hit;
+ (Answer *) createAnswerWithTiming :(NSDate *)paramTiming andXPosition :(float) xPosition
{
NSDate * tempDate = [[NSDate alloc] initWithTimeInterval:0 sinceDate:paramTiming];
Answer * tempAns = [[Answer alloc] init ];
[tempAns setAnswerTiming:tempDate];
[tempDate release];
[tempAns setXPosition:xPosition];
[tempAns setEarlyOrLate:0];
[tempAns setHit:false];
return tempAns;
}
- (void)dealloc {
[answerTiming release];
[self release];
[super dealloc];
}
#end
Error 2) Analyzer says (see below)
- (void)viewDidLoad
{
[super viewDidLoad];
........
...
UIImage * perfectImage = [UIImage imageNamed: #"perfect.png"];
self.perfectImageView2 = [[UIImageView alloc]initWithImage:perfectImage];
// method returns an objective C content with a +1 retain count
[self.perfectImageView2 setFrame:CGRectMake(145.0f,
150.0f,
self.perfectImageView2.image.size.width,
self.perfectImageView2.image.size.height)];
self.view.backgroundColor = [UIColor whiteColor];
UIImage * levelUpImage = [UIImage imageNamed:#"levelup.png"];
self.levelUpImageView = [[UIImageView alloc] initWithImage:levelUpImage];
[self.levelUpImageView setFrame:CGRectMake(100.0f,
400.0f,
self.levelUpImageView.image.size.width,
self.levelUpImageView.image.size.height)];
//object leaked, allocated object is not referenced later in this execution path and has a retain count of +1
self.view.backgroundColor = [UIColor whiteColor];
}
Error 3)
- (NSMutableArray *) generateQuestionTapAnswers:(NSString *) answersString withFirstBeat: (NSDate *) firstBeatTime
{
NSArray * notesToDraw = [answersString componentsSeparatedByCharactersInSet: [NSCharacterSet characterSetWithCharactersInString: #" "]];
float noteValueOffset = 0.0;
NSMutableArray * answerArray = [[NSMutableArray alloc] init ];
// Method returns an objective C object with a +1 retain count
for (int i=1; i < notesToDraw.count; i++) // i = 0 is the time signature
{
.....
}
return answerArray;
// Object returned to caller as an owning reference (single retain count transferred to caller)
// Object leaked: Object allocated and stored into answerArray is returned from a method whose name generateQuestionTapAnswers does not start with copy, mutableCopy
}
Regarding your first and third warnings, the compiler will assume that you'll be creating an object in "a method whose name begins with alloc, new, copy, or mutableCopy" (see Advanced Memory Management Programming Guide). So, if you're returning a +1 object, make sure your method name begins with one of those four prefixes. If you create a +1 object without one of those prefixes, it won't be happy. So, for error #1, if you rename that method to be something like newAnswerWithTiming, that warning should go away. (If the calling method doesn't clean up after them, though, the warning will just be shifted to that routine, but let's take it one step at a time.)
Likewise for error #3, if you rename that method to be something like newAnswerArrayFromQuestionTapAnswers (or whatever ... I'm not sure if I understand your method name ... just make sure it starts with new), you'll similarly eliminate that warning. In both cases, just make sure to release it at the appropriate point because whomever called these two respective methods now "owns" those objects and is responsible for their cleanup.
As an aside, you don't need to do [self release] in your dealloc in your discussion of error #1. (Frankly, I'm surprised it doesn't generate an error.) You don't need it because if self wasn't already at retainCount of zero, you never would have gotten to dealloc in the first place.
Regarding error #2, how are the properties perfectImageView2 and levelUpImageView defined? If retain or strong, you're creating something with a +2 retainCount, and you probably want to add an autorelease after the alloc/init.

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