我正在开发一款至少以3g为目标的ios游戏。我们正在将高清资产用于视网膜显示设备(iphone 4,ipod touch 4th gen)。
内存方面,Ipod Touch 4th gen似乎是我们最受约束的设备,因为它具有与3gs相同的RAM(256与Iphone 4的512相比),但我们正在使用HD资产。该应用程序曾经在尝试加载100-110mb的ram时崩溃,但现在我们已经降到70MB,我们从来没有加载崩溃。
经过大量搜索后,似乎没有正式的硬限制,那么我们应该如何知道要使用什么内存预算才能安全?我们希望能够为艺术家提供他们可以使用的预算,而不会让每个地图都有内存担忧。
答案 0 :(得分:381)
使用实用程序Split编写的测试结果(链接在他的回答中):
设备:(崩溃金额/总金额/总金额百分比)
答案 1 :(得分:129)
我创建了一个小实用程序,它试图分配尽可能多的内存来崩溃,并记录发生内存警告和崩溃的时间。这有助于找出任何iOS设备的内存预算。
答案 2 :(得分:38)
我认为你已经回答了自己的问题:尽量不要超过70 Mb的限制,但这实际上取决于很多事情:你使用的iOS版本(不是SDK),在后台运行的应用程序数量,你正在使用的确切记忆等。
避免即时内存飞溅(例如,你使用40 Mb的RAM,然后为一些简短的计算分配80 Mb的更多)。在这种情况下,iOS会立即终止您的应用程序。
您还应该考虑延迟加载资产(仅在您真正需要时才加载它们,而不是事先加载)。
答案 3 :(得分:17)
在我的应用中,如果使用更多内存,用户体验会更好,所以我必须决定是否真的应该在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(基于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
}
答案 4 :(得分:8)
通过分配SPLITS repo,我构建了一个来测试可以分配给今天的扩展的iOS内存
iOSMemoryBudgetTestForExtension
以下是我在iPhone 5s中获得的结果
内存警告10 MB
应用程序崩溃为12 MB
通过这种方式,Apple只允许任何扩展程序充分发挥其潜力。
答案 5 :(得分:7)
您应该从WWDC 2010 Session videos观看会话147。这是“iPhone OS上的高级性能优化,第2部分” 关于内存优化有很多很好的建议。
一些提示是:
NSAutoReleasePool
来确保您的内存使用不会出现峰值。CGImageSource
。答案 6 :(得分:4)
- (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)
答案 7 :(得分:2)
我通过设备RAM对Jaspers列表进行排序创建了另一个列表(我使用Split&#39的工具进行了自己的测试并修复了一些结果 - 检查我在Jaspers线程中的注释)。
设备RAM:崩溃范围
特殊情况:
可以轻松读取设备RAM:
[NSProcessInfo processInfo].physicalMemory
根据我的经验,1GB设备使用45%,2 / 3GB设备使用50%,4GB设备使用55%是安全的。 macOS的百分比可能会更大。
答案 8 :(得分:0)
从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
答案 9 :(得分:0)
使用上面的许多答案,我实现了适用于iOS 13+的Apples新方法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]);
}
}
重要说明:请不要忘记最后的()
。我在NSLog
方法中同时包含了两个memoryLoggingOutput
选项,因为它不会发出警告您会发现缺少它们并且没有包含方括号会返回一个意外的但持续的结果。
从方法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