[Cocoa + iPhone] Avoiding ‘_handleMemoryWarning:’ Crashes

Spend enough time developing iPhone applications (particularly if you go to the trouble of collecting crash logs from your apps), and eventually you are bound to come across something like the following:

2011-03-04 14:17:48.872 myApp[15129:307] -[UIImageView _handleMemoryWarning:]: unrecognized selector sent to instance 0x2b2060
[Switching to process 11523 thread 0x0]
2011-03-04 14:17:57.034 myApp[15129:307] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIImageView _handleMemoryWarning:]: unrecognized selector sent to instance 0x2b2060'
*** Call stack at first throw:
(
	0   CoreFoundation                      0x3759dc7b __exceptionPreprocess + 114
	1   libobjc.A.dylib                     0x32d9bee8 objc_exception_throw + 40
	2   CoreFoundation                      0x3759f3e3 -[NSObject(NSObject) doesNotRecognizeSelector:] + 98
	3   CoreFoundation                      0x37544467 ___forwarding___ + 506
	4   CoreFoundation                      0x37544220 _CF_forwarding_prep_0 + 48
	5   Foundation                          0x351663b5 _nsnote_callback + 156
	6   CoreFoundation                      0x37520971 __CFXNotificationPost_old + 396
	7   CoreFoundation                      0x37520611 _CFXNotificationPostNotification + 128
	8   Foundation                          0x351556a3 -[NSNotificationCenter postNotificationName:object:userInfo:] + 70
	9   Foundation                          0x3515edbd -[NSNotificationCenter postNotificationName:object:] + 20
	10  UIKit                               0x35a32354 -[UIApplication _performMemoryWarning] + 68
	11  UIKit                               0x35a33450 -[UIApplication _receivedMemoryNotification] + 184
	12  UIKit                               0x35a2ef24 _memoryStatusChanged + 64
	13  CoreFoundation                      0x37552333 __CFNotificationCenterDarwinCallBack + 26
	14  CoreFoundation                      0x37537e37 __CFMachPortPerform + 218
	15  CoreFoundation                      0x3752f5cb __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 28
	16  CoreFoundation                      0x3752f589 __CFRunLoopDoSource1 + 164
	17  CoreFoundation                      0x37521835 __CFRunLoopRun + 580
	18  CoreFoundation                      0x3752150b CFRunLoopRunSpecific + 226
	19  CoreFoundation                      0x37521419 CFRunLoopRunInMode + 60
	20  GraphicsServices                    0x33e76d24 GSEventRunModal + 196
	21  UIKit                               0x3591d57c -[UIApplication _run] + 588
	22  UIKit                               0x3591a558 UIApplicationMain + 972
	23  myApp                             0x00002649 main + 96
	24  myApp                             0x000025b0 start + 52
)
terminate called after throwing an instance of 'NSException'

This error occurs most frequently when suspending an app to the background, but it can also happen randomly at other times as well. Of course, the problem here should be obvious; the crash happens entirely outside of my application code. After the call to ‘main()‘ the stack-trace that leads to the crash lies entirely within framework code. The problem appears to be that some CoreFoundation class is attempting to invoke a ‘_handleMemoryWarning:‘ selector on objects that do not support it.

So how to prevent a crash that happens completely outside of your application code? You might say “use less memory”, but that is not a reliable answer here. The ‘_performMemoryWarning‘ selector may still be invoked no matter how streamlined your memory usage is. The answer, or at least the only answer I’ve found that works reliably, is to use a custom UIApplication subclass and excise the functionality that causes this error, like so:

//
//  SafeUIApplication.h
//  myApp
//
#import <Foundation/Foundation.h>

@interface SafeUIApplication : UIApplication {
}

@end


//
//  SafeUIApplication.m
//  myApp
//
#import "SafeUIApplication.h"

@implementation SafeUIApplication

//these selectors cause crashes in random Foundation classes when the default implementation is called, so instead of crashing our app 
//we ignore these messages, hold on to the memory that has already been allocated to us, and let some other less-clever app crash instead
- (void) _performMemoryWarning {
    NSLog(@"Received a '_performMemoryWarning' call, consider using less memory...");
    return;
}
- (void) _receivedMemoryNotification {
    NSLog(@"Received a '_receivedMemoryNotification' call, consider using less memory...");
    return;
}

@end

Now before the purists and the pedants go jumping down my throat: Yes, this is a hack. Yes, it disables built-in memory-management functionality. Yes, it can mask legitimate problems if your app does in fact have a memory leak (or if it just makes inefficient use of memory). But having built-in memory-management functionality that crashes the app when called is completely unacceptable in my book, and so disabling it is fair game. Further, even with this bit of code disabled, the application will still continue to receive low memory warnings (‘didReceiveMemoryWarning‘ calls) as normal. Hanging on to memory that is already allocated to the app cannot cause the app to crash or behave incorrectly, it just means that less memory is available for other apps that are also running. That seems fair enough to me, at least so long as the default implementation of these methods causes apps to crash.

Anyways, once you’ve added the SafeUIApplication class to your project, using it is as simple as modifying your application’s ‘main.m’ file like so:

//
//  main.m
//  myApp
//
#import <UIKit/UIKit.h>

int main(int argc, char *argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    int retVal = UIApplicationMain(argc, argv, @"SafeUIApplication", @"MyAppDelegateClassName");
    [pool release];
    return retVal;
}

Just specify the SafeUIApplication classname and your UIApplicationDelegate classname, and you no longer need to worry about obscure ‘_handleMemoryWarning:‘ crashes. As noted above, be aware that this will do little to nothing to help applications that have legitimate memory usage issues. But if you are confident that your application’s memory usage is correct and you are still experiencing crashes like the one shown above then this little hack will go a very long way towards keeping your application running and stable.

This entry was posted in coding, objective-c and tagged , , . Bookmark the permalink.

One Response to [Cocoa + iPhone] Avoiding ‘_handleMemoryWarning:’ Crashes

  1. visit us says:

    japan is in a crisis right now

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>