The New iPad: Low Memory Warnings Not Appearing?

2020-02-08 04:44发布

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??

6条回答
老娘就宠你
2楼-- · 2020-02-08 04:52

Mega-Dittos. This has been very reassuring. I too am doing an image intensive app (lots of UIImage objects in UIImageView for animation) and have the usual memory warning code in my app delegate but it is never triggered. Nonetheless, Instruments showed the problem as I loaded images, drew on them and committed them to the imageviews. I'm using ARC, got rid of leaks from CGImageRefs and the like but at the end of the day if you load up enough images in an ImageView, quickly enough you will get a crash without warning of any kind in the log, app delegate method callback, or instruments. The app just gets the rug pulled out from under it without so much as a "by your leave."

Haven't had a chance to try this on an iPad2 yet but regardless, there needs to be SOME indication, at least a minimalist console message or something. Most of the loading happens on my own GCD queue, not the main thread though by definition updates to screen controls have to be done on the main thread. So I supupose if that's blocking when the run is about to be pulled then I guess you can just get an anonymous failure. Sure would help for this to get SOME kind of console message though.

查看更多
不美不萌又怎样
3楼-- · 2020-02-08 04:53

I contacted the Apple Support to solve my memory problem and asked about the low-memory warnings on iPad 3:

-Because memory warnings are delivered on the main thread, your app will not receive memory warnings if it's blocking the main thread.

-Even if your app is not blocking the main thread, it is possible for memory usage to grow quickly enough that memory warnings are not delivered before your app is killed to free memory.

-Memory warnings trigger when the kernel transitions between various levels of memory pressure. Because of this, it is common for an app to receive a memory warning and then be killed quite some time later when memory is exhausted. The initial memory warning freed enough memory to keep the app alive, but not enough for the kernel to transition to a lower level of memory pressure.

Because of all this, memory warning should be treated as a useful data on the state of the hardware and a good guide on how much memory your app should use on a given device, but should not be relied on as a tool to prevent your app from being killed.

Maybe this helps...

查看更多
我命由我不由天
4楼-- · 2020-02-08 04:56

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
查看更多
Anthone
5楼-- · 2020-02-08 04:56

The below is my experience. I am quite happy to be corrected...

How are you loading your images?

If you are using:

[UIImage imageNamed:(NSString *)]

Then you need to make sure that there is a good reason to. If you are making heavy use of an image that needs to be cached, then its a good option. Otherwise I would suggest you use

[UIIMage imageWithContentsOfFile:(NSString *)]

iOS appears to have some issues releasing images loaded via imageNamed, even if there are no longer any references to it. Since your app will no longer have any references to the image, you probably wont get the memory warnings. But that doesn't mean that the memory has been freed. iOS tends to keep these images in memory much longer than you would like. When it would normally give a memory warning, it will just terminate the app.

I would also highly recommend turning on Automatic Reference Counting (ARC).

Edit -> Refactor -> Convert to Objective-C ARC...

I had similar issues for quite a while. Making the above changes to my app stopped the crashes, and stopped the memory leak caused when I reloaded the same image over and over again via imageNamed.

Hope that helps.

查看更多
乱世女痞
6楼-- · 2020-02-08 04:56

I have been working on the app consuming lot of the memory and my suggestions are:

  1. Use alloc - init and release, avoid using autorelease objects.
  2. Check for memory leaks
  3. If you need to use autorelease object, use NSAutoreleasePool before you create the object and drain the pool when you have finished the work with the object.
  4. If you are using OpenGL remember to delete all the created objects, especially textures
  5. Maybe you should try to switch to ARC
查看更多
手持菜刀,她持情操
7楼-- · 2020-02-08 04:58

It sounds to me like the issue is that your images are not being released. According to the UIImage documentation (and my experience), imageNamed: caches the images it loads. Therefore you should use it for small icons or images that are almost constantly used, but it's generally a bad idea to use it for large images or images that are used infrequently. As user499177 says, you should use imageWithContentsOfFile: (you can use [NSBundle mainBundle] methods to derive the path) instead:

This method looks in the system caches for an image object with the specified name and returns that object if it exists. If a matching image object is not already in the cache, this method loads the image data from the specified file, caches it, and then returns the resulting object.

查看更多
登录 后发表回答