NSLocalizedString 从 Swift 传递到 Objective-C 时可能存在 iOS 16 错误

刘ma*_*ell 6 objective-c nslocalizedstring swift ios16

我遇到了我认为是 iOS16 中的一个错误:当本地化字符串从 Swift 传递到 Objective-C 并与另一个相同的本地化字符串(在 Objective-CC 中定义)进行比较时,结果可能为 false 并且参数顺序可以影响结果。看演示:

\n
class ViewController: UIViewController {\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        let tc = TestClass()\n        tc.receive(NSLocalizedString("Start", comment:""))\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n
@implementation TestClass\n- (void)receive:(NSString *)swiftString {\n    NSString *objcString = NSLocalizedString(@"Start", @"");\n    BOOL result1 = [swiftString isEqualToString:objcString];\n    BOOL result2 = [objcString isEqualToString:swiftString];\n    NSLog(@"result: %d, %d", result1, result2);\n}\n@end\n
Run Code Online (Sandbox Code Playgroud)\n

它是可本地化的(以日语为例,但拉丁语以外的任何书写系统都可以重现该错误):

\n
"Start" = "\xe9\x96\x8b\xe5\xa7\x8b";\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
result: 0, 1\n
Run Code Online (Sandbox Code Playgroud)\n

我们不知道造成这种情况的根本原因是来自NSLocalizedString()还是-isEqualToString。iOS15 上不会发生这种情况。

\n

还有其他人遇到过这个错误吗?

\n

Mar*_*n R 6

这看起来绝对是一个错误,我可以在 Xcode iOS 16 模拟器中重现它。调试器显示获得的字符串

NSString *objcString = NSLocalizedString(@"Start", @"");
Run Code Online (Sandbox Code Playgroud)

是 的实例_NSBPlistMappedString,是 的未记录子类NSString

(lldb) p objcString
(_NSBPlistMappedString *) $1 = 0x8230ceeb696930f7
(lldb) p [objcString superclass]
(Class) $2 = NSString
Run Code Online (Sandbox Code Playgroud)

显然,iOS 16 中 Swift 字符串与该子类实例的比较未正确实现。以下是两种可能的解决方法:

解决方法#1:使用compare而不是isEqualToString

NSString *objcString = NSLocalizedString(@"Start", @"");
BOOL result1 = [swiftString compare:objcString] == NSOrderedSame;
BOOL result2 = [objcString compare:swiftString] == NSOrderedSame;
NSLog(@"result: %d, %d", result1, result2);
// result: 1, 1
Run Code Online (Sandbox Code Playgroud)

解决方法#2:确保它objcString是以下实例NSString

NSString *objcString = @(NSLocalizedString(@"Start", @"").UTF8String);
BOOL result1 = [swiftString isEqualToString:objcString];
BOOL result2 = [objcString isEqualToString:swiftString];
NSLog(@"result: %d, %d", result1, result2);
// result: 1, 1
Run Code Online (Sandbox Code Playgroud)

当然,这两种解决方法都不是很令人满意。正如评论中所建议的,该错误应该报告给 Apple