5

I have created an app and it is (well it was) ready to submit.

The app is basically a flash cards apps for kids where there are 12 cards per page.

It is currently iPad only and when the cards are touched a image view animates out of the card to full screen with a full resolution 2048 x 1536 and it plays a sound of the animal, you tap the full size image and it goes back in the card. Each card has 5 images assigned to it. The app has been working perfectly all though the design period and now that I have loaded all of the animals and I have been testing it and noticed it crashed.

Long story short the memory continually grows by 12MB per image (although each image is around 200-400KB) until it reaches 700MB of Memory and it crashes. This memory never reduces.

I have run the app through the xcode instruments and can see no leaks but have identified in the 'Allocations' that it is the 'ImageIO-jpeg-data' category that is the culprit.

I have spent all evening reading up about this as I have found some promising results however these results have not worked for me so I am assuming that I have misunderstood what has been advised or I have not understood so was hoping that someone could maybe help me out or explain in laymen terms how I can resolve this.

OK so to explain how I have the view contoller set up.

Originally I set up the 5 images for each card in an array but there was a delay so someone on here recommended that I pre load the images in viewDidLoad and then call them when the card/button was touched.

So for example I was using this format in viewDidLoad

domestic100 = [UIImage imageNamed:@"ferret-00"];
domestic101 = [UIImage imageNamed:@"ferret-01"];
domestic102 = [UIImage imageNamed:@"ferret-02"];
domestic103 = [UIImage imageNamed:@"ferret-03"];
domestic104 = [UIImage imageNamed:@"ferret-04"];

and then in the the button pressed I assigned the image to the image view by calling:

domestic01ImageContainer.image = domestic100; 

inside a if else look checking if the image number was 1 to 4.

Anyway it worked perfectly (apart form this memeory issue)

I have read online that I would be better off declaring the uiimages using

[UIImage imageWithContentsOfFile:path]

as seen in this post: UIImage memory not deallocating VM: ImageIO_JPEG_DATA?

So the alterations I have made in my code are:

in my viewDidLoad I now create uiimages as so:

domestic200 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"dog-00" ofType: @"jpg"]];
domestic201 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"dog-01" ofType: @"jpg"]];
domestic202 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"dog-02" ofType: @"jpg"]];
domestic203 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"dog-03" ofType: @"jpg"]];
domestic204 = [UIImage imageWithContentsOfFile: [[NSBundle mainBundle] pathForResource: @"dog-04" ofType: @"jpg"]];

and then in the button pressed call the image in to the imageview using

[domestic01ImageContainer setImage:domestic100];

I am getting the same results - the image are just building up in the same memory and not releasing or deallocating.

I'm no wiz when it comes of objective-c and this is my first real app.

If someone could be kind enough to spare a moment of their time to help me I would be really appreciative.

Thanks in advance.

****** Edit ******

So I have told you how I load my images above, I also in the viewDidLoad give the 12 imageviews a starting point and a hidden alpha.

domestic01ImageContainer = [[UIImageView alloc] initWithFrame:CGRectMake(30, 123, 152, 224)];
domestic01ImageContainer.Alpha = 0;

I then add the subview with:

[self.view addSubview:domestic01ImageContainer];

each imageview is allocated a tap gesture to hide the image at a later point:

UITapGestureRecognizer *domestic01Tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageTaped:)];
[domestic01ImageContainer addGestureRecognizer:domestic01Tap];
[domestic01ImageContainer setUserInteractionEnabled:YES];

When the thumb / card is pressed a streamlined version of the button would be:

- (IBAction)domestic01ButtonClicked:(id)sender {
static int imageNumber = 0;

if (imageNumber == 0) {
    [domestic01ImageContainer setImage:domestic100];
    imageNumber++;
}

else if (imageNumber == 1) {
    [domestic01ImageContainer setImage:domestic101];
    imageNumber++;
}

else if (imageNumber == 2) {
    [domestic01ImageContainer setImage:domestic102];
    imageNumber++;
}

else if (imageNumber == 3) {
    [domestic01ImageContainer setImage:domestic103];
    imageNumber++;

}

else if (imageNumber == 4) {
    [domestic01ImageContainer setImage:domestic104];
    imageNumber = 0;
}

domestic01ImageContainer.Alpha = 1;

[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        domestic01ImageContainer.frame = CGRectMake(0, 0, 768, 1024);
    } completion:^(BOOL finished) {

}];

}

The in my imageTapped recognizer to shrink the full size image back to the card I use:

[UIView animateWithDuration:0.2 delay:0.0 options:UIViewAnimationOptionCurveEaseIn animations:^{
        domestic01ImageContainer.frame = CGRectMake(30, 123, 152, 224);
    } completion:^(BOOL finished) {
        domestic01ImageContainer.Alpha = 0;
    }];

Which basically put the flash card back from full screen to the size of the card / thumbnail and the sets alpha again to 0.

Like I said it all works but as you have suggested the image is being retained somewhere.

Hope this sheds a bit more light on what could be causing the issue.

I had tried setting the image to nil in after the alpha was set to 0 in the imageTapped but this didn't clear the memory.

[domestic05ImageContainer setImage:nil];
Community
  • 1
  • 1
Dan Davies
  • 169
  • 2
  • 12
  • Sounds like your images are being retained somewhere you don't expect. Are you assigning the images to more than one variable or property? – Anna Dickinson Dec 12 '14 at 22:20
  • Hi Anna, I will add an edit to my question so you have more info. Thanks – Dan Davies Dec 13 '14 at 07:41
  • How many images do you have loaded at any given time? I can't quite tell from your description. If you have 12 x 5 images loaded at once, that's 750 MB. Keep in mind that images are *uncompressed* when you assign them to an imageView, and your images are 12.5MB each. You need to delay loading images until they are needed. – Anna Dickinson Dec 13 '14 at 16:56
  • Another thought (though I'm not 100% sure about it): your gesture recognizers retain the "self" object you're passing. If self retains domesticImageContainer, then you have a retain cycle. You could fix that by passing a weak reference to self when you create the gesture recognizer. – Anna Dickinson Dec 13 '14 at 17:01
  • Hi Anna - yea so there is 12 x 5 images in total but I only need one at a time. The user taps a card and the image appears full screen, when they tap the full screen image it should disappear and is not needed again until it comes back around - so 4 other images need to be displayed from that flash card before it is required again. So my issue is why is it not freeing up the memory when it is not required? The memory increases slowly when each image is pressed so it appears to me that the images are not all using the memory at one time, i press the first and the memory goes up to 12MB, – Dan Davies Dec 13 '14 at 17:46
  • I press the next image and it goes up to 24, then the next image foes to 36 and so on and so on until the limit is reached and the app crashes. Thats all i want is so when the tapgesturerecognizer is pressed the image is released from memory. – Dan Davies Dec 13 '14 at 17:47

2 Answers2

6

Ok so if anyone else is suffering from this issue then here is the solution that I found that work perfectly.

http://xcodenoobies.blogspot.co.uk/2011/02/best-way-to-use-uiimageview-memory.html

Thanks Anna for your input, it pointed me in the right direct but his was the answer that did it for me.

Dan Davies
  • 169
  • 2
  • 12
1

I was having a similar issue. Memory was being held on abnormally. I had a UIImageView IBOutlet and I was setting its image property with my view controller's UIImage property, which I would set while initializing my view controller. The UIImageView would hold on to the UIImage property of my view controller wrongly even when I would go back to the root view controller.

I fixed the issue by changing the view controller's UIImage property to NSData and setting the UIImageView's image property as follows:

[_previewImageView setImage:[UIImage imageWithData:_previewImageData]];

This resolved the issue of ImageIO-jpeg-data being help on unnecessarily. Hope this helps someone.