100

I'd like to test my app functions well in low memory conditions, but it's difficult to test. How can I induce low memory warnings that trigger the didReceiveMemoryWarning method in my views when the app is running on the device, not the simulator? Or what are some ways I can test my app under these possible conditions?

The reason I can't use the simulator is my app uses Game Center and invites don't work on the simulator.

Cœur
  • 37,241
  • 25
  • 195
  • 267
BeachRunnerFred
  • 18,070
  • 35
  • 139
  • 238

10 Answers10

285

You can call the private method:

[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)];

Just remember to use it on debug only, or else your app will get rejected.

Enzo Tran
  • 5,750
  • 6
  • 31
  • 36
  • 6
    It appears this code accurately simulates the memory warning on the device. Thanks Enzo! – William Denniss Feb 22 '12 at 09:59
  • I've tried out this code and placed it in one of my view controller's viewDidAppear. How do I know if I'm already simulating my app on low memory? I've looked on my logs yet I don't see a warning. – Grauzten Nov 27 '12 at 03:08
  • @Grauzten I don't see any logs, but I can verify on iOS 6.0.1 it definitely sends the warning message to the app (and the app's controllers). – Ben Kreeger Mar 12 '13 at 16:40
  • @Enzo Tran: Can you please briefly explain how can I use this? or share any tutorial link to refer same. – Mrunal Jul 23 '13 at 18:41
  • If i want to test how Application 1 behaves in low memory condition, can i add this private method in Application 2 and run both the applications so that Application 1 will receive low memeory warning ? Will this private method send low memory signal to all applications on the device or only to the application where this method is included ? Please clarify – Prem Nov 20 '13 at 17:17
  • 33
    Instead of using this in code, execute it in the debugger. Just hit pause and enter `po [[UIApplication sharedApplication]performSelector:@selector(_performMemoryWarning)]` – orkoden Jan 06 '14 at 17:39
  • 18
    Rather than using po, use expr, i.e. ```expr (void)[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)];``` – Andy Mar 05 '14 at 11:22
  • 13
    In the debugger, you don't need to use the workaround for not having the header: `expr (void)[[UIApplication sharedApplication] _performMemoryWarning]` – OrangeDog Aug 18 '15 at 09:44
  • 1
    If you need to debug a crash happening when handling memory warnings, the following could help: `expr (void)[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning) withObject:nil afterDelay:1.0]` – Sea Coast of Tibet Mar 04 '16 at 08:07
  • Is there any good way to suppress the warning of using the private API? – Dmitry Jul 08 '16 at 04:25
  • 3
    For anyone who is looking to do this with Swift: type `expr UIApplication.sharedApplication().performSelector("_performMemoryWarning")` instead of using the usual `#selector` – funct7 Sep 20 '16 at 01:58
  • Although _performMemoryWarning triggers calls to methods such as applicationDidReceiveMemoryWarning, it does not create a memory pressure event that a DISPATCH_SOURCE_TYPE_MEMORYPRESSURE dispatch source will see, unlike the simulator's menu option. – jk7 Dec 28 '16 at 16:55
  • 1
    @Mrunal and others who wanna know how to use this: implement the said method `[[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)];` in your .m file, also create a `-(void) didReceiveMemoryWarning` that prints out something. if you put the said method in an ibaction, it'll trigger when you tap on the button, console will print out what you put in didReceiveMemoryWarning – JackyW Mar 07 '17 at 05:11
  • 2
    For Swift 3: `UIApplication.shared.perform(Selector(("_performMemoryWarning")))` – neave Jun 01 '17 at 08:40
  • 2
    Swift 5: UIApplication.shared.perform(Selector(("_performMemoryWarning"))) – StackUnderflow Feb 24 '20 at 10:32
29

The iOS Simulator's Simulate Memory Warning menu item allows you to simulate a memory warning.

enter image description here

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Ole Begemann
  • 135,006
  • 31
  • 278
  • 256
  • Only way to check it on the device is to actually force it, ie by using the app for long periods of time or intensive images or what have you, will vary app to app. A suggestion might be to use an older device (3g perhaps?) instead of an iPhone 4 to help you get to the warning faster. – Jesse Naugher Jan 17 '11 at 19:56
  • Thanks, Jesse, I think I'm just gonna add some hacks to my app to simulate invites so that I can run it in the simulator and induce the memory warnings that way. – BeachRunnerFred Jan 17 '11 at 20:04
  • 16
    Question was about the simulation on device. – KPM Feb 23 '15 at 19:09
22

Using Instruments, use the menu item: Instrument -> Simulate Memory Warning.

To use Instruments on your app from Xcode, use the Product -> Profile menu item.

ThomasW
  • 16,981
  • 4
  • 79
  • 106
14

I've re-written Enzo Tran's answer in Swift:

UIControl().sendAction(Selector(("_performMemoryWarning")), to: UIApplication.shared, for: nil)
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86
14

If someone, for whatever reason, tries to do this in Swift 4 - here is how to allocate 1.2 GB of ram.

let d = Data.init(repeating: 100, count: 1200000000)
  • This is helpful to trigger a warning alert in other apps
Blazej SLEBODA
  • 8,936
  • 7
  • 53
  • 93
  • Will doing something like this obfuscate the actual memory information I'm trying to view in Instruments? While I see there isn't a proper way to do this on a physical device, if my point is to see what is taking up my memory within my app it seems like this is going to skew all of the data and I'll no longer have a relative answer. I'll still see what is taking up *more*, but not the true proportions. – Jake T. Aug 06 '18 at 20:21
  • this crashes immediately –  Jun 17 '20 at 17:31
10

To test on a device, just add some code that periodically allocates large chunks of memory without freeing it (i.e. leak on purpose). You can do this in a separate thread, or in response to a timer, or using whatever mechanism that best allows you to test and observe the behavior of your application.

You might also choose to create a separate app that does something similar and is designed to run in the background, if you'd like to easily reuse this and/or test with multiple applications.

BinaryStar
  • 268
  • 2
  • 5
  • 7
    not very practical. the solution to call a private method for testing is better – Daij-Djan May 01 '13 at 21:50
  • 5
    Not a good solution if you want to look at real world scenario. You are crippling the app due to a leak not due to memory pressure. That is bad since what you want to do is test how the app responds in high memory pressure state. On the downside its harder to figure out what the problem is a real leak or the fake one you introduced. The private method one is better for testing as given below. – fzaziz Apr 17 '14 at 08:57
  • 2
    This answer should not be downvoted, as the second paragraph is actually what Apple says to do if you want to test your app's behavior when memory really gets low. – matt Aug 30 '15 at 01:02
  • 1
    Answer would be improved with a code block to perform this allocation! – pkamb Dec 08 '16 at 21:28
  • Although it takes more work to setup, this is a more realistic test than calling the private method. When there really is critical memory pressure, will your app be able to perform the actions you tell it to do? And will those actions relieve the situation or make it worse and cause iOS to terminate it? Besides, the private method does not work for testing DISPATCH_SOURCE_TYPE_MEMORYPRESSURE. – jk7 Dec 28 '16 at 17:54
7

Converted @ChikabuZ to swift 3:

UIControl().sendAction(Selector(("_performMemoryWarning")), to: UIApplication.shared, for: nil)
Csabi
  • 3,097
  • 17
  • 59
  • 107
kindaian
  • 147
  • 1
  • 2
6

If someone, for whatever reason, tries to do this in Swift 5 - here is how to allocate 1.2 GB of RAM:

for _ in 0...1200 {
    var p: [UnsafeMutableRawPointer] = []
    var allocatedMB = 0
    p.append(malloc(1048576))
    memset(p[allocatedMB], 0, 1048576);
    allocatedMB += 1;
}
stackich
  • 3,607
  • 3
  • 17
  • 41
ph1lb4
  • 1,982
  • 17
  • 24
5

Theres a menu command that will invoke it.

Hardware > Simulate Memory Warning from the simulator.

Daniel A. White
  • 187,200
  • 47
  • 362
  • 445
4

Swift 4:

UIApplication.shared.perform(Selector(("_performMemoryWarning")))

Can execute the above in response to an event/notification. For example:

    Button(action: {
        UIApplication.shared.perform(Selector(("_performMemoryWarning")))
    }, label: {
        Image(systemName: "memorychip")
    })
aone
  • 57
  • 8
Vishal Chaudhry
  • 2,834
  • 1
  • 22
  • 8