Ios 应用程序最大内存预算

我正在开发一款 ios 游戏,目标是至少3G。我们正在使用高清资产的视网膜显示设备(iphone4,ipod 触摸第四代)。

在内存方面,Ipod Touch 4th gen 似乎是我们最受限制的设备,因为它的 RAM (256相对于 Iphone 4的512)和3gs 一样大,但是我们在它上面使用了高清设备。这个应用程序过去在试图加载100-110MB 内存时会崩溃,但是现在我们已经减少到70MB 了,我们从来没有加载崩溃过。

经过大量的搜索,似乎没有官方的硬限制,所以我们应该如何去了解什么内存预算使用是安全的?我们希望能够给艺术家一个预算,他们可以使用没有记忆的担忧为每张地图。

119487 次浏览

我想你已经回答了自己的问题: 尽量不要超过70Mb 的限制,但是这真的取决于很多事情: 你使用的是什么 iOS 版本(而不是 SDK) ,有多少应用程序在后台运行,你使用的内存是多少等等。

只要避免即时内存飞溅(例如,您正在使用40Mb 的 RAM,然后为一些较短的计算分配80Mb 的更多内存)。在这种情况下,iOS 会立即终止您的应用程序。

您还应该考虑延迟加载资产(只在真正需要的时候加载,而不是事先加载)。

你应该看看 WWDC 2010年会议视频的第147部分,它是“ iPhone 操作系统的高级性能优化,第2部分”。
有许多关于内存优化的好建议。

以下是一些小贴士:

  • 使用嵌套的 NSAutoReleasePool确保内存使用不会激增。
  • 使用 CGImageSource时,创建大图像缩略图。
  • 响应低内存警告。

我创建了一个小工具,它试图分配尽可能多的内存崩溃,并记录当内存警告和崩溃发生时。这有助于找出任何 iOS 设备的内存预算。

Https://github.com/split82/iosmemorybudgettest

Split 实用程序的测试结果写道(链接在他的回答中) :

设备: (崩溃量/总量/总量的百分比)

  • IPad1:127MB/256MB/49%
  • IPad2:275MB/512MB/53%
  • IPad3:645MB/1024MB/62%
  • IPad4:585MB/1024MB/57% (iOS 8.1)
  • IPad Mini 第一代: 297MB/512MB/58%
  • IPad Mini retina: 696MB/1024MB/68% (iOS 7.1)
  • IPad Air: 697MB/1024MB/68%
  • IPad Air 2:1383MB/2048MB/68% (iOS 10.2.1)
  • IPad Pro 9.7”: 1395MB/1971MB/71% (iOS 10.0.2(14A456))
  • IPad Pro 10.5”: 3057/4000/76% (iOS 11 beta 4)
  • IPad Pro 12.9”(2015) : 3058/3999/76% (iOS 11.2.1)
  • IPad Pro 12.9”(2017) : 3057/3974/77% (iOS 11 beta 4)
  • IPad Pro 11.0”(2018) : 2858/3769/76% (iOS 12.1)
  • IPad Pro 12.9”(2018,1 TB) : 4598/5650/81% (iOS 12.1)
  • IPad 10.2:1844/2998/62% (iOS 13.2.3)
  • IPod touch 第四代: 130MB/256MB/51% (iOS 6.1.1)
  • IPod touch 第五代: 286MB/512MB/56% (iOS 7.0)
  • IPhone4:325MB/512MB/63%
  • IPhone4:286MB/512MB/56%
  • IPhone5:645MB/1024MB/62%
  • IPhone5:646MB/1024MB/63%
  • IPhone6:645MB/1024MB/62% (iOS 8.x)
  • IPhone6 + : 645MB/1024MB/62% (iOS 8.x)
  • IPhone6s: 1396MB/2048MB/68% (iOS 9.2)
  • IPhone6s + : 1392MB/2048MB/68% (iOS10.2.1)
  • IPhone: 1395MB/2048MB/69% (iOS 9.3)
  • IPhone7:1395/2048MB/68% (iOS 10.2)
  • IPhone7 + : 2040MB/3072MB/66% (iOS10.2.1)
  • IPhone8:1364/1990MB/70% (iOS 12.1)
  • IPhone X: 1392/2785/50% (iOS 11.2.1)
  • IPhone XS: 2040/3754/54% (iOS 12.1)
  • IPhone XS Max: 2039/3735/55% (iOS 12.1)
  • IPhone XR: 1792/2813/63% (iOS 12.1)
  • IPhone 11:2068/3844/54% (iOS 13.1.3)
  • IPhone 11 Pro Max: 2067/3740/55% (iOS 13.2.3)
  • IPhone 12 Pro: 3054/5703/54% (iOS 16.1) iPhone 12专业版: 3054/5703/54% (iOS 16.1)

在我的应用程序,用户体验是更好的,如果更多的内存使用,所以我必须决定,如果我真的应该释放 所有的内存,我可以在 didReceiveMemoryWarning。根据 Split 和 Jasper Pol 的回答,最多使用总设备内存的45% 似乎是一个安全阈值(谢谢大家)。

如果有人想看看我的实际执行情况:

#import "mach/mach.h"


- (void)didReceiveMemoryWarning
{
// Remember to call super
[super didReceiveMemoryWarning];


// If we are using more than 45% of the memory, free even important resources,
// because the app might be killed by the OS if we don't
if ([self __getMemoryUsedPer1] > 0.45)
{
// Free important resources here
}


// Free regular unimportant resources always here
}


- (float)__getMemoryUsedPer1
{
struct mach_task_basic_info info;
mach_msg_type_number_t size = sizeof(info);
kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS)
{
float used_bytes = info.resident_size;
float total_bytes = [NSProcessInfo processInfo].physicalMemory;
//NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
return used_bytes / total_bytes;
}
return 1;
}

Swift (基于 这个答案) :

func __getMemoryUsedPer1() -> Float
{
let MACH_TASK_BASIC_INFO_COUNT = (sizeof(mach_task_basic_info_data_t) / sizeof(natural_t))
let name = mach_task_self_
let flavor = task_flavor_t(MACH_TASK_BASIC_INFO)
var size = mach_msg_type_number_t(MACH_TASK_BASIC_INFO_COUNT)
var infoPointer = UnsafeMutablePointer<mach_task_basic_info>.alloc(1)
let kerr = task_info(name, flavor, UnsafeMutablePointer(infoPointer), &size)
let info = infoPointer.move()
infoPointer.dealloc(1)
if kerr == KERN_SUCCESS
{
var used_bytes: Float = Float(info.resident_size)
var total_bytes: Float = Float(NSProcessInfo.processInfo().physicalMemory)
println("Used: \(used_bytes / 1024.0 / 1024.0) MB out of \(total_bytes / 1024.0 / 1024.0) MB (\(used_bytes * 100.0 / total_bytes)%%)")
return used_bytes / total_bytes
}
return 1
}
- (float)__getMemoryUsedPer1
{
struct mach_task_basic_info info;
mach_msg_type_number_t size = MACH_TASK_BASIC_INFO;
kern_return_t kerr = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &size);
if (kerr == KERN_SUCCESS)
{
float used_bytes = info.resident_size;
float total_bytes = [NSProcessInfo processInfo].physicalMemory;
//NSLog(@"Used: %f MB out of %f MB (%f%%)", used_bytes / 1024.0f / 1024.0f, total_bytes / 1024.0f / 1024.0f, used_bytes * 100.0f / total_bytes);
return used_bytes / total_bytes;
}
return 1;
}

如果使用 TASK _ BASIC _ INFO _ COUNT 而不是 MACH _ TASK _ BASIC _ INFO,您将获得

Kerr = = KERN _ INVALID _ ARGUMENT (4)

通过分支 SPLITS 回购,我建立了一个测试 iOS 内存,可以分配给今天的扩展

扩展

以下是我在 iPhone5s 中得到的结果

内存警告为10MB

应用程序崩溃在12MB

通过这种方式,苹果只是允许任何扩展以其最大的潜力发挥作用。

我通过设备 RAM 对 Jaspers 列表进行排序创建了另一个列表(我使用 Split 的工具进行了自己的测试,并修复了一些结果——检查我在 Jaspers 线程中的注释)。

设备内存: 崩溃百分比范围

  • 256 MB: 49%-51%
  • 512 MB: 53%-63%
  • 1024 MB: 57%-68%
  • 2048 MB: 68%-69%
  • 3072 MB: 63%-66%
  • 4096 MB: 77%
  • 6144 MB: 81%

特殊情况:

  • IPhone X (3072 MB) : 50%
  • IPhone XS/XS Max (4096 MB) : 55%
  • IPhone XR (3072 MB) : 63%
  • IPhone 11/11 Pro Max (4096 MB) : 54% -55%

设备内存可以很容易地读取:

[NSProcessInfo processInfo].physicalMemory

根据我的经验,对于1GB 设备使用45% 是安全的,对于2/3 GB 设备使用50% ,对于4GB 设备使用55% 。MacOS 的百分比可以稍微大一点。

从 iOS13开始,有一种苹果支持的方法可以通过使用

#include <os/proc.h>


size_t os_proc_available_memory(void)

介绍: Https://developer.apple.com/videos/play/wwdc2019/606/

大概29分钟左右。

编辑: 添加到文档的链接 Https://developer.apple.com/documentation/os/3191911-os_proc_available_memory?language=objc

根据上面的许多答案,我实现了 Apple 针对 iOS 13 + 的新方法 os_proc_available_memory()NSByteCountFormatter,后者提供了许多有用的格式选项,可以更好地输出内存:

#include <os/proc.h>


....


- (NSString *)memoryStringForBytes:(unsigned long long)memoryBytes {
NSByteCountFormatter *byteFormatter = [[NSByteCountFormatter alloc] init];
byteFormatter.allowedUnits = NSByteCountFormatterUseGB;
byteFormatter.countStyle = NSByteCountFormatterCountStyleMemory;
NSString *memoryString = [byteFormatter stringFromByteCount:memoryBytes];
return memoryString;
}


- (void)memoryLoggingOutput {
if (@available(iOS 13.0, *)) {
NSLog(@"Physical memory available: %@", [self memoryStringForBytes:[NSProcessInfo processInfo].physicalMemory]);
NSLog(@"Memory A (brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory()]);
NSLog(@"Memory B (no brackets): %@", [self memoryStringForBytes:(long)os_proc_available_memory]);
}
}

重要提示: 不要忘记最后的 ()我已经在 memoryLoggingOutput方法中包含了这两个 NSLog选项,因为它没有警告您它们丢失了,并且没有包含括号将返回一个意外但是常量的结果。

从方法 memoryStringForBytes返回的字符串输出如下值:

NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.93 GB
// 2 seconds later
NSLog(@"%@", [self memoryStringForBytes:(long)os_proc_available_memory()]); // 1.84 GB