如何给一个真正的iPhone空区域设置

2022-01-29 09:18:47 标签 iosobjective-clocale

我有一个iOS应用程序,可以得到这样的国家代码

NSLocale *currentLocale = [NSLocale autoupdatingCurrentLocale];
NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode];

我理解技术上这可以是空的,但我(错误地)假设这只能发生在不重要的角落情况下。

但现在我有一个用户,他的手机报告为空locale,它破坏了我的应用程序的一部分,所以他们不能使用它。在进行bug修复时,我只是想了解如何进入和离开这种状态,以便他们能够围绕bug工作。但即使他们将自己的语言和地区设置为英语/美国,他们仍然会在语言和地区屏幕上得到这个奇怪的结果:

在我的应用程序中,由于null locale/region的错误仍然发生。根据维基百科,这是底部的0xa4“货币符号”:

货币符号¤是一种不指定货币的符号。

所以即使是iOS也不理解他们的语言环境。这在真正的手机上是如何发生的?你又该如何解决它?

他们使用的是iOS 14。7。1上的iPhone 12 mini。

###这是一个不幸的、有点罕见的bug,似乎与用户偏好在设备上被破坏有关,没有一个已知的原因,你不能做什么,除非尝试检测它,并返回到一些默认的locale(如en_US).

)。

深入挖掘(来自Hopper的细节):

& lt; Foundation>+[NSLocale autoupdatingCurrentLocale]创建一个NSAutoLocale的实例,一个私有类的实例,它的初始化器获取+[NSLocale currentLocale]并侦听一个私有定义的通知,当当前区域设置改变时:

                     +[NSLocale autoupdatingCurrentLocale]:
00000000000bdfd8         stp        x29, x30, [sp, #-0x10]!                     ; Objective C Implementation defined at 0x24fb80 (class method)
00000000000bdfdc         mov        x29, sp
00000000000bdfe0         adrp       x8, #0x353000                               ; &@selector(variantFormatter)
00000000000bdfe4         ldr        x0, [x8, #0xf58]                            ; objc_cls_ref_NSAutoLocale,_OBJC_CLASS_$_NSAutoLocale
00000000000bdfe8         bl         imp___stubs__objc_opt_new                   ; objc_opt_new
00000000000bdfec         ldp        x29, x30, [sp], #0x10
00000000000bdff0         b          imp___stubs__objc_autorelease               ; objc_autorelease
int -[NSAutoLocale _init](int arg0) {
    r19 = arg0;
    r0 = [NSLocale currentLocale];
    r0 = [r0 retain];
    r8 = 0x35603c;
    asm { ldpsw      x8, x9, [x8] };
    *(r19 + r9) = r0;
    pthread_mutex_init(r19 + r8, 0x0);
    [[NSNotificationCenter defaultCenter] addObserver:r19 selector:@selector(_update:) name:@"kCFLocaleCurrentLocaleDidChangeNotification-4" object:0x0];
    r0 = r19;
    return r0;
}

NSAutoLocale响应所有的区域设置方法并将它们转发给底层的NSLocale实例因此我们可以查看+currentLocale返回什么

& lt; CoreFoundation>+[NSLocale currentLocale]简单地返回_CFLocaleCopyCurrent() (NSLocale和CFLocaleRef是免费桥接的)的结果:

void +[NSLocale currentLocale]() {
    [_CFLocaleCopyCurrent() autorelease];
    return;
}

_CFLocaleCopyCurrent()返回一个“Guts”函数的共享实现的内容,该函数从多个地方用不同的参数调用:

int _CFLocaleCopyCurrent() {
    rax = __CFLocaleCopyCurrentGuts(0x0, 0x1, 0x0, 0x0);
    return rax;
}

& lt; CoreFoundation>__CFLocaleCopyCurrentGuts太大了,不能在这里包含内联,但是当使用给定的参数调用(并且当它没有缓存的“当前区域设置”实例)时,它会通过从当前用户首选项查找AppleLocale键的值来创建一个新的区域设置

如果没有,它也会AppleLanguages dictionary and tries to create a 字典,并试图从其中一个值创建语言环境,但这是独立的

因此,“当前区域设置”实际上只是一个区域设置对象,由prefs中“AppleLocale”定义的区域标识符构造。具体的位置是kCFPreferencesAnyApplication应用程序(即全局prefs)的kCFPreferencesCurrentUser在kCFPreferencesCurrentHost - i。e。什么是返回的默认读/写NSGlobalDomain…(相当于默认的read/write -g…)

您可以自己检查这个值,因为我的机器在美国被设置为英语

$ defaults read -g AppleLocale
en_US

您可以编写一个小工具来检查“AppleLocale”,将一个值写入“AppleLocale”并重新运行它来检查更改,如果您愿意的话,但这也可以在进程内进行(以一种令人费解的方式)。如果你在Objective-C的某个地方提前声明_CFLocaleResetCurrent()(桥接头会起作用),你可以直接调用清除当前区域缓存的函数,并通过操作CFPreferences直接看到当前区域的变化:

import Foundation
func overwriteLocaleIdentifier(_ identifier: String) {
    // We use kCFPreferencesCurrentApplication to avoid changing system-wide settings.
    CFPreferencesSetAppValue("AppleLocale" as CFString, identifier as CFString, kCFPreferencesCurrentApplication)
    _CFLocaleResetCurrent()
}
func withOverwrittenLocaleIdentifier(_ identifier: String, _ action: () -> Void) {
    let currentIdentifier = Locale.current.identifier
    defer { overwriteLocaleIdentifier(currentIdentifier) }
    overwriteLocaleIdentifier(identifier)
    action()
}
print(Locale.current.identifier)
withOverwrittenLocaleIdentifier("he_IL") {
    print(Locale.current.identifier)
}
print(Locale.current.identifier)

对我来说,这产生了

en_US
he_IL
en_US

无论好坏,这种行为意味着无论为“AppleLocale”键找到什么值,都将被用作当前区域设置标识符。

如果“AppleLocale”值丢失,你会开始看到一些日志:

2021-09-15 16:47:52.013846-0400 LocaleExample[40427:9913027] [User Defaults] CFPrefsPlistSource<0x107b08420> (Domain: kCFPreferencesAnyApplication, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): Value for key AppleLocale was (null). Expected en_US (defaults(40182): 2021-09-15 16:44:29 (EDT))

当区域标识符丢失(或设置为无意义)时,系统绝对会开始不正常,因为区域标识符是直接使用的。

那么这什么时候会发生呢?在典型的iOS设备上,答案应该是“决不”,但事实并非如此。通过典型的UI,没有办法意外地设置locale标识符为无效的东西,但我看到野生用户的locale设置为“”“en”(没有国家代码),或只是在设备上确认为股票,而不是越狱等。它真正需要的是一个错误的过程来打击“AppleLocale”值,你可以在这种情况下结束。

通常情况下,除了退回到一个已知的好的本地语言环境(或像en_US这样的默认设置),如果用户请求通过Settings(更改设置,然后再更改回设置)来重置locale之外,没有任何实际的办法。理想情况下,这将使他们回到一个已知的良好状态。

阅读全文

▼ 版权说明

相关文章也很精彩
推荐内容
更多标签
相关热门
全站排行
随便看看

错说 cuoshuo.com —— 程序员的报错记录

部分内容根据CC版权协议转载;网站内容仅供参考,生产环境使用务必查阅官方文档

辽ICP备19011660号-5

×

扫码关注公众号:职场神器
发送: 1
获取永久解锁本站全部文章的验证码