ChatGPT解决这个技术问题 Extra ChatGPT

ios应用最大内存预算

我正在开发一款至少以 3gs 为目标的 ios 游戏。我们将高清资产用于视网膜显示设备(iphone 4、ipod touch 4th gen)。

内存方面,Ipod Touch 第 4 代似乎是我们最受限制的设备,因为它具有与 3gs 相同数量的 RAM(256 与 Iphone 4 的 512 相比),但我们在其上使用高清资源。该应用程序在尝试加载 100-110mb 的内存时会崩溃,但现在我们已降至 70MB,我们从未遇到过加载崩溃。

经过大量搜索,似乎没有官方硬限制,那么我们应该如何知道使用什么内存预算才能安全?我们希望能够给艺术家一个他们可以使用的预算,而不用担心每张地图的内存问题。

不知道这个问题如何与稍后提出的问题重复。

J
Jasper

使用实用程序拆分的测试结果写道(链接在他的答案中):

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

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)


iPhone4:类似的值已确认,似乎是合法的:P
iPhone 5 在 ±645 MB 时崩溃。
@JasperPol 我已经编辑了您的帖子以包含我拥有的各种设备,我希望没关系。我已经添加了我测试过的 iOS 版本以防万一,但如果您认为它不重要,请随时将其删除。
太棒了,这个列表已经创建和维护了。根据我的经验,为了安全起见,我不得不将内存保持得更低,可能是此处显示的 20%。设备与设备之间的差异也是高度可变的。
刚刚在 12.9 iPad Pro 上运行。内存警告为 2451MB,崩溃为 3064MB,总计 3981MB。
S
Split

我创建了一个小型实用程序,它试图分配尽可能多的内存以使其崩溃,并记录内存警告和崩溃发生的时间。这有助于找出任何 iOS 设备的内存预算。

https://github.com/Split82/iOSMemoryBudgetTest


我做了一个有趣的测试:用 xcode 监控内存使用情况运行我的应用程序,进入后台,运行 BudgetTest。测试被杀死,而我的后台应用程序没有。我有兴趣知道为什么。此外,这与@cprcrack 在另一个答案中所说的背道而驰。
M
Max

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

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

您还应该考虑延迟加载资产(仅在您真正需要时才加载它们,而不是事先加载)。


只是我们想放尽可能多的东西(图形和声音)。艺术家们总是希望尽可能多地投入到游戏中,这就是为什么我想用预算限制他们。我想我们只需要在不同设置下的许多不同设备上进行测试,就能找到一个合理的最大内存占用。
在该设备上的任何时候(即使在其他需要大量内存的应用程序中大量使用之后)只分配 70MB(可能低于预算)总是能保证分配成功,还是可能仍然崩溃?
@Steven Lu 这取决于您的设备。例如,在较新的设备上,如 iPhone5 或 iPad4 70 Mb 分配根本不是问题。
是的,但我想知道我是否可以确定只要我的应用程序的总使用量保持在神奇设备特定的内存预算之下,它就不会被终止!
没有保证
C
Community

在我的应用程序中,如果使用更多内存,用户体验会更好,因此我必须决定是否真的应该释放 所有 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
}

size 应该是 TASK_BASIC_INFO_COUNT 而不是 sizeof(info) - 这个错误用相同的代码复制粘贴到许多地方
谢谢斯派克斯。根据 this link,您似乎是对的。您还有其他可以找到此信息的参考吗?
苹果使用 TASK_BASIC_INFO_COUNT too
45% 不再是安全限制,它太接近 iPhone X 的 50% 崩溃值。我建议使用 40%,或者为每个设备单独设置值。
J
Jasper

通过 fork SPLITS repo,我构建了一个来测试可以分配给 Today's Extension 的 iOS 内存

iOSMemoryBudgetTestForExtension

以下是我在 iPhone 5s 中得到的结果

10 MB 内存警告

应用程序在 12 MB 时崩溃

通过这种方式,Apple 只是允许任何扩展发挥其全部潜力。


K
Kobski

您应该从 WWDC 2010 Session videos 观看会话 147。这是“iPhone OS 上的高级性能优化,第 2 部分”。
关于内存优化有很多很好的建议。

一些提示是:

使用嵌套的 NSAutoReleasePools 确保您的内存使用量不会激增。

从大图像创建缩略图时使用 CGImageSource。

响应内存不足警告。


我的问题不是关于如何优化(尽管感谢链接),而是关于我们可以允许自己使用多少。原因是,例如,如果我们优化以获得 20mb,那么如果它在合理的“预算”范围内,艺术家会想要使用这 20mb,也就是确保它不会导致任何性能问题或内存崩溃。
好的。崩溃将是因为操作系统由于内存受限而终止应用程序。您可以在 didReceiveMemoryWarning 中添加一个 NSLog,然后在分配不同数量的内存时进行一些测试,然后查看内存警告何时开始出现。
E
Exaberri Tokugawa

从 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


最后!我在少数设备上测试了 os_proc_available_memory(),结果与上面大表中的值非常相似!
D
Dmitry Preobrazhenskiy
- (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)


您至少应该提到您的答案几乎是上面@cprcrack 的精确复制和粘贴。唯一的区别是 TASK_BASIC_INFO_COUNT。
S
Slyv

我通过按设备 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 的百分比可能会大一些。


更新:似乎 iPhone X 是一个例外 - 当使用 50% 的 RAM 时它会崩溃(使用 iOSMemoryBudgetTest 应用程序测试)。我更新了列表。
A
App Dev Guy

使用上面的许多答案,我已经为 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

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅