-2

I created an array using the following code. After the 12 images have no longer needed, I set the imageArray to nil and reload a new set of images into this array. When I run the app in instruments I can see a memory buildup issue. I also ran heapshots and it shows 12 images still lingering even after I set the array to nil. I also tried to init this array in its own autorelease pool thinking it was somehow created on a separated thread below the main thread. That did not work either. Any ideas?

ViewController.h

@property (strong, nonatomic) NSMutableArray *imageArray;

ViewController.m

- (void) firstSetOfImages{
  imageArray= [[NSMutableArray alloc] initWithObjects:wordImage1, wordImage2, wordImage3, wordImage4, wordImage5, wordImage6, wordImage7, wordImage8, wordImage9, wordImage10, wordImage11, wordImage12, nil];
}

- (void) clearImages{
  [self setImageArray:nil];
  [imageArray removeAllObjects];
  [self secondSetOfImages];
}

- (void) secondSetOfImages{
    imageArray= [[NSMutableArray alloc] initWithObjects:wordImage1, wordImage2, wordImage3, wordImage4, wordImage5, wordImage6, wordImage7, wordImage8, wordImage9, wordImage10, wordImage11, wordImage12, nil];
}

Here is an example of 1 heapshot taken in between the loading of 1 set of 12 images and the second set of 12 images.

Snapshot    Timestamp   Heap Growth # Persistent
Heapshot 3  00:39.457.174   53.02 KB    800
 < non-object >     26.05 KB    277
 UIImageView        3.38 KB 36
 CFDictionary (mutable)     3.38 KB 72
 CFBasicHash (key-store)        2.83 KB 73
 CFBasicHash (value-store)      2.83 KB 73
 NSPathStore2       2.25 KB 12
 CGImageReadRef     1.88 KB 12
 CALayer        1.69 KB 36
 CGImage        1.62 KB 13
 CFNumber       1.31 KB 84
 CGImagePlus        1.31 KB 12
 CFData     1.12 KB 24
 CGImageProvider        768 Bytes   12
 CGDataProvider     720 Bytes   5
 UIImage        576 Bytes   12
 CFString (immutable)       416 Bytes   13
 CFArray (mutable-variable)     384 Bytes   12
 CGImageReadSessionRef      192 Bytes   12
 _   UIImageViewExtendedStorage     192 Bytes   4
 __NSArrayM     160 Bytes   5   
     CFDictionary (immutable)       48 Bytes    1

EDIT:

I modified the code and made the arrays an ivar. I took another sample of Allocations in Instruments. Below are is a more detailed display of my heapshots. I took a heapshot every time I reset my array with 12 new images. Every heapshot is has a heapgrowth of about 35kb.

Snapshot    Timestamp       Heap Growth     # Persistent
Heapshot 4  00:58.581.296       35.63 KB        680
 < non-object >                 13.02 KB        220
 CFDictionary (mutable)         3.38 KB         72
 CFBasicHash (key-store)        2.81 KB     72
 CFBasicHash (value-store)      2.81 KB     72
 NSPathStore2                   2.28 KB     12
 CGImageReadRef                 1.88 KB     12
 CGImage                        1.50 KB     12
 CFNumber                       1.31 KB     84
 CGImagePlus                1.31 KB     12   
 CFData                     1.12 KB     24
 UIImageView                1.12 KB     12
 CGImageProvider            768 Bytes       12
 UIImage                    576 Bytes       12
 CALayer                    576 Bytes       12
 CFString (immutable)       384 Bytes       12
 CFArray (mutable-variable) 384 Bytes       12
 CGImageReadSessionRef      192 Bytes       12
 CGDataProvider             144 Bytes       1
 _UIImageViewExtendedStorage96 Bytes        2
 __NSArrayM                 32 Bytes        1

Here is the stacktrace of one of those Persistent items in UIImage. It doesn't point to a specific line of code that created it. Not sure where to go from here?

  24 FourGameCenter 0x4b4bf
  23 FourGameCenter 0x4b538
  22 UIKit UIApplicationMain
  21 GraphicsServices GSEventRunModal
  20 CoreFoundation CFRunLoopRunInMode
  19 CoreFoundation CFRunLoopRunSpecific
  18 CoreFoundation __CFRunLoopRun
  17 CoreFoundation __CFRunLoopDoSource1
  16 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
  15 GraphicsServices PurpleEventCallback
  14 GraphicsServices _PurpleEventCallback
  13 UIKit _UIApplicationHandleEvent
  12 UIKit -[UIApplication sendEvent:]
  11 UIKit -[UIWindow _sendTouchesForEvent:]
  10 UIKit -[UIControl touchesEnded:withEvent:]
   9 UIKit -[UIControl(Internal) _sendActionsForEvents:withEvent:]
   8 UIKit -[UIControl sendAction:to:forEvent:]
   7 UIKit -[UIApplication sendAction:toTarget:fromSender:forEvent:]
   6 UIKit -[UIApplication sendAction:to:from:forEvent:]
   5 FourGameCenter 0x8d7fa
   4 FourGameCenter 0x71830
   3 FourGameCenter 0x797e6
   2 libobjc.A.dylib _objc_rootAllocWithZone
   1 libobjc.A.dylib class_createInstance
   0 libsystem_c.dylib calloc
ctw
  • 97
  • 1
  • 11

4 Answers4

2

You can't do the following in clearImages:

[self setImageArray:nil];
[imageArray removeAllObjects];

In the snippet above, you've just set imageArray to nil. You can't then send the nil object a removeAllObjects message: it'll just silently do nothing.

You need to reorder your lines to:

[imageArray removeAllObjects];
[self setImageArray:nil];
sam-w
  • 7,478
  • 1
  • 47
  • 77
  • Thanks for your suggestion. I just tried to reorder the lines but I still see the memory buildup. – ctw May 07 '13 at 11:26
  • Can we see some more code please? For instance where is `wordImage1` coming from? – sam-w May 07 '13 at 11:28
  • UIImage *wordImage1 = [[UIImage alloc] initWithContentsOfFile:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent: @"Image1.png"]]; – ctw May 07 '13 at 11:29
  • wordImageView = [[UIImageView alloc] initWithImage:[wordImageArray objectAtIndex:(imageNumber - 1)]]; – ctw May 07 '13 at 11:32
  • Here are a couple of snippets showing how I load the images and then how I pull it up and to a UIImageView. Do you need anything else? Also, I am using a similar array with an audio file. I don't think it's causing the problem, but I'd thought I'd mention that. – ctw May 07 '13 at 11:33
  • Are you leaking memory every time you change the array, or just the first time? – sam-w May 07 '13 at 12:26
  • The first line [self setImageArray:nil]; eliminates both the array and the contents. The second line is unnecessary. – ahwulf May 07 '13 at 12:56
  • Is wordImageView a property? – ahwulf May 07 '13 at 14:10
  • @ahwulf - yes it is a property. – ctw May 07 '13 at 15:44
1
[self setImageArray:nil]

will remove the image objects automatically, so no need to do the removeObjects.

However if you are using those images in UIImageViews elsewhere, and those UIImageViews are retained or used in a view elsewhere, you will need to release those/remove the UIImageView from the superview as well.

[imageView removeFromSuperview];

Or if you have the UIImageViews in an array:

[imageViewArray makeObjectsPerformSelector:@selector(removeFromSuperview)];
stevekohls
  • 2,214
  • 23
  • 29
  • Thanks for the response. I added [imageView removeFromSuperview] in there at the same time that I set the imageArray to nil and it is still leaking. – ctw May 07 '13 at 13:58
  • You may need to release the imageView as well: `imageView = nil;` – stevekohls May 07 '13 at 14:31
  • I think we need to see more of your code. Perhaps from where you are using the imageViews? – stevekohls May 08 '13 at 17:25
0

Check to see if you have zombies turned on in the scheme:diagnostics. EVen though Zombies are unlikely under ARC, the project might still have this turned on, which makes checking for memory leaks look like everything is leaking.

ahwulf
  • 2,584
  • 15
  • 29
0

You are mixing metaphors here - you use a property but set it like an ivar - its possible that creates a leak. Also, if you release the array, you don't have to release every object as it will do that for you.

So, use an ivar

@MyClass...
{
  NSMutableArray *imageArray;
}

Now you can set it to new arrays, and for sure the old array is released. If you want to release the array completely, just set it to nil.

Alternately, if you want to use properties, then when you change the object, use:

self.imageArray = ...;

Now, a second problem - your code accesses imageArray directly (no leading "_") - did you synthesize the ivar using the same name? This is probably not a good idea as it just confuses people when they read your code. If you just drop the @synthesize, you now get the ivar "for free" as the property name with a "_" prepended. You can directly access that ivar but anyone reading your code will know its attached to a property.

EDIT: You obviously are not releasing the objects you think you are. Your problem is quite similar to this one. What you need to do is as follows:

1) create a UIImage subclass, and add a log statement in the dealloc. Instead of creating UIImages, you are going to create MyImages (the subclass), which means you'll need your subclass .h file anywhere you want to use it (pr put it in the pch file while debugging). Do this first to see if the images get released. If not you know your problem.

2) Subclass NSArray (NOT NSMutableArray, not you don't really need mutable arrays in the code you show above). If there are reasons that you must use mutable arrays, then when you set the ivar, use [MyArray arrayWithArray:theMutableArray] first. Log the dealloc.

Now run your tests. Either the array, or the images, is not getting released. Once you know which, you can track down the retain issue.

There is an old saying, I'll butcher it, but it goes something like, when you try everything that can possible be the cause of a problem, and that doesn't fix it, then it has to be something that you assume is NOT the problem that is actually it.

Community
  • 1
  • 1
David H
  • 40,852
  • 12
  • 92
  • 138
  • I changed the array to an ivar. Didn't work. I reset everything as a property...still leaking. I edited my post above to provide more detail as to what my heapshots are showing. – ctw May 07 '13 at 20:07
  • Thanks for your help. I put all the UIImages in a MyImages subclass. THe images were indeed released. But I still had a leak so I took out the NSArray (yes there is no need for a MutableArray). But there still seems to be a leak. So as you said, it must be something that I assumed was being released but is not. I don't have any other images in the class, so I'm not sure what it is. The only way I was able to clear the memory was by releasing the View Controller. I was able to make it work, though not as cleanly as I would have liked. – ctw May 08 '13 at 21:23
  • @ctw you really should carry this to the end - you will for sure learn an important lesson on coding for Apple. When you are done with the images, did you set the array to nil (which is what releasing the view controller is doing). If releasing the view controller fixes it, then it MUST be some other object that the view controller is retaining that retains the array. Look at every single object that touches that controller. If there is nothing proprietary in that one single class, email it to me and I'll take a quick look. I've been doing OSX/iOS for many years, and tracking down teaches u. – David H May 08 '13 at 21:38
  • Thank you for the offer to look at it! I will clean it up and send it to you. – ctw May 09 '13 at 10:48