我正在开发一款至少以 3gs 为目标的 ios 游戏。我们将高清资产用于视网膜显示设备(iphone 4、ipod touch 4th gen)。
内存方面,Ipod Touch 第 4 代似乎是我们最受限制的设备,因为它具有与 3gs 相同数量的 RAM(256 与 Iphone 4 的 512 相比),但我们在其上使用高清资源。该应用程序在尝试加载 100-110mb 的内存时会崩溃,但现在我们已降至 70MB,我们从未遇到过加载崩溃。
经过大量搜索,似乎没有官方硬限制,那么我们应该如何知道使用什么内存预算才能安全?我们希望能够给艺术家一个他们可以使用的预算,而不用担心每张地图的内存问题。
使用实用程序拆分的测试结果写道(链接在他的答案中):
设备:(崩溃量/总量/占总量的百分比)
iPad1:127MB/256MB/49%
iPad2:275MB/512MB/53%
iPad3:645MB/1024MB/62%
iPad4:585MB/1024MB/57% (iOS 8.1)
iPad Mini 第 1 代:297MB/512MB/58%
iPad Mini 视网膜:696MB/1024MB/68% (iOS 7.1)
iPad 空气: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 beta4)
iPad Pro 12.9” (2015): 3058/3999/76% (iOS 11.2.1)
iPad Pro 12.9” (2017): 3057/3974/77% (iOS 11 beta4)
iPad Pro 11.0” (2018): 2858/3769/76% (iOS 12.1)
iPad Pro 12.9” (2018, 1TB): 4598/5650/81% (iOS 12.1)
iPad 10.2:1844/2998/62% (iOS 13.2.3)
iPod touch 第 4 代:130MB/256MB/51% (iOS 6.1.1)
iPod touch 第 5 代:286MB/512MB/56% (iOS 7.0)
iPhone4:325MB/512MB/63%
iPhone4s:286MB/512MB/56%
iPhone5:645MB/1024MB/62%
iPhone5s: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%(iOS 10.2.1)
iPhoneSE:1395MB/2048MB/69%(iOS 9.3)
iPhone7:1395/2048MB/68%(iOS 10.2)
iPhone7+:2040MB/3072MB/66%(iOS 10.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)
我创建了一个小型实用程序,它试图分配尽可能多的内存以使其崩溃,并记录内存警告和崩溃发生的时间。这有助于找出任何 iOS 设备的内存预算。
https://github.com/Split82/iOSMemoryBudgetTest
我想你已经回答了你自己的问题:尽量不要超过 70 Mb 的限制,但这实际上取决于很多事情:你使用的是什么 iOS 版本(不是 SDK),后台运行了多少应用程序,确切的内存是多少你正在使用等
只是避免瞬间内存飞溅(例如,您使用 40 Mb 的 RAM,然后分配 80 Mb 的更多用于一些简短的计算)。在这种情况下,iOS 会立即终止您的应用程序。
您还应该考虑延迟加载资产(仅在您真正需要时才加载它们,而不是事先加载)。
在我的应用程序中,如果使用更多内存,用户体验会更好,因此我必须决定是否真的应该释放 所有 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;
}
斯威夫特(基于 this answer):
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
}
通过 fork SPLITS repo,我构建了一个来测试可以分配给 Today's Extension 的 iOS 内存
iOSMemoryBudgetTestForExtension
以下是我在 iPhone 5s 中得到的结果
10 MB 内存警告
应用程序在 12 MB 时崩溃
通过这种方式,Apple 只是允许任何扩展发挥其全部潜力。
您应该从 WWDC 2010 Session videos 观看会话 147。这是“iPhone OS 上的高级性能优化,第 2 部分”。
关于内存优化有很多很好的建议。
一些提示是:
使用嵌套的 NSAutoReleasePools 确保您的内存使用量不会激增。
从大图像创建缩略图时使用 CGImageSource。
响应内存不足警告。
didReceiveMemoryWarning
中添加一个 NSLog
,然后在分配不同数量的内存时进行一些测试,然后查看内存警告何时开始出现。
从 iOS13 开始,有一种 Apple 支持的查询方式,方法是使用
#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
- (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)
我通过按设备 RAM 对 Jaspers 列表进行排序创建了另一个列表(我使用 Split 的工具进行了自己的测试并修复了一些结果 - 检查我在 Jaspers 线程中的评论)。
设备 RAM:崩溃的百分比范围
256MB:49% - 51%
512MB:53% - 63%
1024MB:57% - 68%
2048MB:68% - 69%
3072MB:63% - 66%
4096MB:77%
6144MB:81%
特别案例:
iPhone X (3072MB):50%
iPhone XS/XS Max (4096MB):55%
iPhone XR (3072MB):63%
iPhone 11/11 Pro Max (4096MB):54% - 55%
设备 RAM 可以轻松读取:
[NSProcessInfo processInfo].physicalMemory
根据我的经验,1GB 设备使用 45%,2/3GB 设备使用 50%,4GB 设备使用 55% 是安全的。 macOS 的百分比可能会大一些。
使用上面的许多答案,我已经为 iOS 13+ 实现了 Apple 的新方法 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
不定期副业成功案例分享