iosmemory-managementipad-3

The New iPad: Low Memory Warnings Not Appearing?


I have been developing an application for iPad that is very graphically intensive. I have been able to squeeze quite a bit of performance out already on the iPad 2, but the @2x graphics for the new iPad are packing quite the punch in the memory department. Using the Activity Monitor in Instruments I am able to see the size of my application mushrooming into the 300MB-400MB range but I do not receive any low memory notifications. I am using a UINavigationController to manage my views, so getting down into the stack has a cumulative effect on memory that ends in its eventual termination. I do not experience this problem on the iPad 2, where I receive low memory notifications as expected. My app has been coded to clean up as much as possible and performs very well on that device.

I have read a number of similar questions asked:

IOS app killed for Low Memory but no Memory Warning received
iPhone app uses 150 MB memory and still no low memory warning!

None of the suggestions seem to help.

I have inserted code to force a low-memory notification to be sent:

[[UIApplication sharedApplication] _performMemoryWarning];

This does cause the inactive views to unload as expected and returns memory consumption to normal. This uses a private API and is hack, so for practical reasons is not a solution. How do I get my device to properly respond to low memory conditions and let my app know that it needs to clean up??


Solution

  • This problem was fixed in iOS 5.1.1. For those users who are on 5.1, I have implemented my own memory watchdog and sending out a notification similar to the one used when a real memory warning is issued.

    I first created a category on UIApplication. This posts a notification that UIImage listens for (or whatever its backing cache is) to unload cached images.

    .h

    @interface UIApplication (ForceLowMemory)
    
    + (void) forceUnload;
    
    @end
    

    .m

    #import "UIApplication+ForceLowMemory.h"
    
    @implementation UIApplication (ForceLowMemory)
    
    + (void)forceUnload {
        [[NSNotificationCenter defaultCenter] postNotificationName:UIApplicationDidReceiveMemoryWarningNotification 
                                                            object:[UIApplication sharedApplication]];
    }
    
    @end
    

    Next, I created a memory manager watchdog like the following:

    .h

    @interface ELMemoryManager : NSObject
    
    - (void)startObserving;
    - (void)stopObserving;
    
    + (ELMemoryManager*) sharedInstance;
    
    @end
    

    .m

    #import "ELMemoryManager.h"
    #import "UIApplication+ForceLowMemory.h"
    #import <mach/mach.h>
    
    @interface ELMemoryManager()
    
    @property (nonatomic, retain) NSTimer *timer;
    uint report_memory(void);
    
    @end
    
    #define MAX_MEM_SIZE 475000000
    
    @implementation ELMemoryManager
    @synthesize timer = timer_;
    
    static ELMemoryManager* manager;
    
    #pragma mark - Singleton
    
    + (void) initialize {
        if (manager == nil) {
            manager = [[ELMemoryManager alloc] init];
        }
    }
    
    + (ELMemoryManager*) sharedInstance {
        return manager;
    }
    
    #pragma mark - Instance Methods
    
    uint report_memory(void) {
        struct task_basic_info info;
        mach_msg_type_number_t size = sizeof(info);
        kern_return_t kerr = task_info(mach_task_self(),
                                       TASK_BASIC_INFO,
                                       (task_info_t)&info,
                                       &size);
        if( kerr == KERN_SUCCESS ) {
            return info.resident_size;
        } else {
            return 0;
        }
    }
    
    - (void)startObserving {
        if (!self.timer) {
            NSTimer* timer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:@selector(checkMemory:) userInfo:nil repeats:YES];
            self.timer = timer;
        }
        [self.timer fire];
    }
    
    - (void)stopObserving {
        [self.timer invalidate];
        self.timer = nil;
    }
    
    - (void)checkMemory:(id)sender {
        uint size = report_memory();
        if (size > MAX_MEM_SIZE) {
            NSLog(@"we've busted the upper limit!!!");
            [UIApplication forceUnload];
        }
    }
    
    #pragma mark - Memory Management
    - (void)dealloc {
        [self.timer invalidate];
        [timer_ release];
        [super dealloc];
    }
    
    @end