Reputation: 435
I have a custom UIView
called ActivityDetailView
that I instantiate and then add to a scrollview within a parent view controller. When this custom view is allocated, it takes up about 1mb each time of additional memory and Instruments is showing that the memory is never being released even though the view and the parent view controller each have their dealloc
methods being called. I am getting memory warnings and the app is eventually getting killed so I'm obviously doing something wrong.
Updated w/ info about map view being the cause, but I need a fix
Within the custom ActivityDetailView
nib file, there is a map view that is zoomed and centered around the users's location. When I removed this map view from the nib so that it doesn't draw on screen, the memory allocation issues went away. However, I obviously need the map view. Why would the map view's data not be released when the map view goes out of scope?
There is only 1 ActivityDetailView
and 1 ActivityDetailViewController
alive when the view is showing. As soon as I pop the view off the stack, they are no longer living. Doesn't make sense how the memory keeps growing even though the objects are being killed as shown via Instruments. If the parent views are deallocated, why isn't the map view data being deallocated?
What am I doing wrong or what should I check?
Here is the custom view:
@interface ActivityDetailView ()
{
CLLocation *location;
__weak id parentViewController;
int scrollViewX;
ImageUtility *imageUtility;
}
@end
@implementation ActivityDetailView
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
NSArray *xibViews = [[NSBundle mainBundle] loadNibNamed:@"ActivityDetailView" owner:nil options:nil];
if ([xibViews count] < 1) return nil;
ActivityDetailView * xibView = [xibViews objectAtIndex:0];
[xibView setFrame:frame];
self = xibView;
}
return self;
}
- (id)initWithLocation:(CLLocation *)loc parentController:(id)parent
{
self = [self initWithFrame:CGRectMake(0, 0, 320, 1000)];
if (self)
{
imageUtility = [ImageUtility sharedManager];
location = loc;
parentViewController = parent;
scrollViewX = 0;
[self centerMapForActivityLocation];
[self addPhotoButtonWithImageNamed:@"addActivityPhoto.png" target:parentViewController selector:@selector(addPhotoToActivity:)];
}
return self;
}
- (void)dealloc
{
NSLog(@"ActivityDetailView was dealloced");
}
In the parent view controller:
@interface ActivityDetailViewController ()
{
ActivityDetailView *detailView;
}
@end
@implementation ActivityDetailViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Some code left out for clarity
[self setupView];
}
- (void)didReceiveMemoryWarning
{
NSLog(@"Purging image cache");
[[ImageUtility sharedManager] purgeCache];
}
- (void)dealloc
{
// These essentially do nothing to help the problem
detailView.mapView = nil;
[detailView removeFromSuperview];
detailView = nil;
self.scrollView = nil;
NSLog(@"ActivityDetailViewController was dealloced");
}
- (void)setupView
{
// Add the activity detail view to the scroll view
detailView = [[ActivityDetailView alloc] initWithLocation:self.activityLocation parentController:self];
[self.scrollView addSubview:detailView];
self.scrollView.contentSize = detailView.frame.size;
// Setup the map view
detailView.mapView.delegate = self;
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.coordinate = self.activityLocation.coordinate;
[detailView.mapView addAnnotation:annotation];
if (self.activity.mapImageName) {
detailView.mapView.scrollEnabled = YES;
detailView.mapView.zoomEnabled = YES;
} else {
detailView.mapView.scrollEnabled = NO;
detailView.mapView.zoomEnabled = NO;
}
// Add the weather area to the view
dayView = [[DailyButtonView alloc] initWithFrame:CGRectMake(0, -17, 60, 70)];
dayView.hidden = YES;
[detailView.weatherView addSubview:dayView];
}
Here's an image from Instruments showing that the majority of memory is from the nib loading
I don't have anything to cache at the moment. The view is basically a scrollview with a mapview inside of it and some labels and a couple of table views. The table views aren't populated with anything and I'm not loading any images within the view other than the background image for the scrollview, but that should get released when the view does.
Upvotes: 9
Views: 9311
Reputation: 46703
As it turns out, there is a known bug with iOS 6 and MKMapView
not releasing it's memory correctly. Unfortunately there is no real fix other than trying to force the map view to purge it's cache which doesn't have that great of an impact on releasing memory.
The strange thing is that even when the app is receiving memory warnings, the map view cache is still not being purged properly. Eventually, the app becomes unstable and is killed by the OS.
Apple has been getting very sloppy with their testing lately. This is the second bug with MKMapView
that I've come across (the other being mapViewDidFinishLoadingMap:
being called early) and both bugs have been really obvious to catch if they had just done some performance and sanity testing.
Here is some code which may help:
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
// This is for a bug in MKMapView for iOS6
[self purgeMapMemory];
}
// This is for a bug in MKMapView for iOS6
// Try to purge some of the memory being allocated by the map
- (void)purgeMapMemory
{
// Switching map types causes cache purging, so switch to a different map type
detailView.mapView.mapType = MKMapTypeStandard;
[detailView.mapView removeFromSuperview];
detailView.mapView = nil;
}
The other thing you could do is use one instance of the MKMapView
throughout your entire app and that should help minimize how much memory is allocated each time the view is shown since the map view will already be allocated.
Upvotes: 22
Reputation: 2335
You are probably facing a problem with cache.
If you are using a lot of images maybe a good option is to remove this images from the xib and add it using code. Or if you load the images using [UIImage imageNamed:@""]
But use something like that:
NSData* dataImg = [NSData dataWithContentsOfFile:@"yourfile.png"];
UIImage* img = [UIImage imageWithData:dataImg];
To better help I need to know how you start your tables and also the others features.
Upvotes: 0
Reputation: 2898
This is not really the best way to create a custom view from your nib file. What I suggest you do is to add the view that you take from the .nib file as a subview to self. The code should look something like this.
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
NSArray *xibViews = [[NSBundle mainBundle] loadNibNamed:@"ActivityDetailView" owner:nil options:nil];
if ([xibViews count] < 1) return nil;
ActivityDetailView * xibView = [xibViews objectAtIndex:0];
[xibView setFrame:frame];
[self addSubview:xibView]; // This is where I have changed your code
}
return self;
}
Upvotes: 0