Objective-C调用[NSLocale currentLocale]时App在特定设备偶发崩溃
问题描述
使用Objective-C开发iOS应用时,调用[NSLocale currentLocale]获取当前系统区域会触发App崩溃。该问题无法在全量设备复现,仅在某台特定的iPhone 13 Pro真机上触发,其余同型号iPhone 13 Pro真机均无该异常;且应用完全不支持模拟器运行,无法通过模拟器调试定位,需可行的解决方案。
崩溃采集信息
Crashlytics采集的崩溃堆栈如下:
Crashed: com.apple.main-thread EXC_BAD_ACCESS KERN_PROTECTION_FAILURE 0x000000016bcfffb0 CoreFoundation _CFLocaleCopyCurrentGuts + 4 SKCountryPicker.m - Line 74 -[SKCountryPicker defaultCountryCode] + 74 SKCountryPicker.m - Line 81 -[SKCountryPicker getCountryDetailBasedOnSIMAndIOSSettings] + 81 SKCountryPicker.m - Line 94 -[SKCountryPicker getCountryDetailForCountryCode:] + 94 SKCountryPicker.m - Line 81 -[SKCountryPicker getCountryDetailBasedOnSIMAndIOSSettings] + 81 SKCountryPicker.m - Line 94 -[SKCountryPicker getCountryDetailForCountryCode:] + 94 SKCountryPicker.m - Line 87 -[SKCountryPicker getCountryDetailBasedOnSIMAndIOSSettingsForCountryList:] + 87 SKCountryPicker.m - Line 65 __49-[SKCountryPicker getCountryListSuccess:failure:]_block_invoke_2 + 65 Foundation __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 4330003460 Foundation -[NSBlockOperation main] + 4330004024 Foundation __NSOPERATION_IS_INVOKING_MAIN__ + 4330003460 Foundation -[NSOperation start] + 4330004024 Foundation __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 4330003460 Foundation __NSOQSchedule_f + 4330004024 libdispatch.dylib _dispatch_block_async_invoke2 + 4330003460 libdispatch.dylib _dispatch_client_callout + 4330004024 libdispatch.dylib _dispatch_main_queue_drain + 4330003460 libdispatch.dylib _dispatch_main_queue_callback_4CF + 4330004024 CoreFoundation __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 4330003460
崩溃类型为内存访问错误,触发点在CoreFoundation内部的_CFLocaleCopyCurrentGuts函数。
对应崩溃源码
崩溃堆栈对应源码第74、81行实现如下:
-(NSString*)defaultCountryCode { NSLocale *currentLocale = [NSLocale currentLocale]; NSString *countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; return countryCode; } -(SKCountry*)getCountryDetailBasedOnSIMAndIOSSettings { SKCountry *selectedCountryData = [self getCountryDetailForCountryCode: [self defaultCountryCode]]; return selectedCountryData; }

解决方案
- 修复多线程访问风险:
[NSLocale currentLocale]在部分iOS系统版本中,跨线程并发调用时可能触发底层缓存访问异常。当前堆栈逻辑由NSOperationQueue调度最终切回主线程执行,可强制将获取Locale的逻辑固定在主线程同步执行,避免跨线程访问问题。
参考修改代码:-(NSString*)defaultCountryCode { __block NSString *countryCode = nil; if ([NSThread isMainThread]) { NSLocale *currentLocale = [NSLocale currentLocale]; countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ NSLocale *currentLocale = [NSLocale currentLocale]; countryCode = [currentLocale objectForKey:NSLocaleCountryCode]; }); } // 兜底默认值,可根据产品需求替换 if (!countryCode) { countryCode = @"US"; } return countryCode; } - 排查SKCountryPicker内部递归问题:从堆栈可见
getCountryDetailBasedOnSIMAndIOSSettings和getCountryDetailForCountryCode:存在互相递归调用的情况,需检查两个方法的逻辑,补充递归终止条件,避免无限递归导致栈溢出、内存越界访问受保护页。 - 兼容非官方系统修改场景:单台设备复现的系统API崩溃,大概率是设备开启越狱环境、安装了区域/定位修改类插件,篡改了系统Locale相关的底层数据,导致系统API返回异常对象。可增加
@try-@catch异常捕获兜底,同时提供备用的区域获取逻辑,比如通过[[NSUserDefaults standardUserDefaults] objectForKey:@"AppleLocale"]获取区域标识后解析国家码,绕过异常的系统API路径。 - 真机定位方式:无需模拟器,直接将异常的iPhone 13 Pro连接Xcode,添加
EXC_BAD_ACCESS断点和全局异常断点,触发崩溃时可直接查看内存状态、寄存器信息,确认崩溃根因。
内容的提问来源于stack exchange,提问作者Sachin Mishra
相关产品推荐
相关产品推荐

