NSString initWithBytesNoCopy 在使用 NSUTF32StringEncoding 编码时返回 nil

问题描述

我正在开发 C++ 和 Swift 之间的桥梁。所以我需要将 C++ u32 字符串转换为 swift 字符串。这是有问题的代码

    int count = linesProvider->getdisplayLinesCount();
    NSMutableArray *lines = [[NSMutableArray alloc] initWithCapacity:static_cast<NSUInteger>(count)];

    for (int i = 0; i < count; ++i) {
        std::u32string_view line = linesProvider->getdisplayedLineAt(i);
        Nsstring* objCline = [[Nsstring alloc] initWithBytesNocopy:(void*)line.data()
                                                            length:line.size() * sizeof(char32_t)
                                                          encoding:NSUTF32StringEncoding freeWhenDone:NO];
        [lines addobject:objCline];
    }

在调试时我发现 objCline 为零。做错了什么?

我的调试方法如下:

enter image description here

解决方法

如果您使用该方法的 initWithBytes: 变体,您的字符串数据是否被接受?

失败的原因是您提供了 CFString 不喜欢的字符串数据(在这种情况下上述复制变体也会失败),或者 CFString 根本不支持UTF-32 作为内部表示,因此必须始终复制它们。我怀疑是后者。

如果您想确定,可以在此处搜索旧版 CFStringCreateWithBytesNoCopy() 函数(NSString 最终调用的函数)的源代码:

https://opensource.apple.com/source/CF/CF-1151.16/CFString.c.auto.html

它调用了相当长的 __CFStringCreateImmutableFunnel3() 函数,需要一段时间才能解开。您可以编译您自己的该函数版本,并在调试器中逐步执行以找出导致 return NULL 的情况。

(顺便说一句,我怀疑 …NoCopy 语义在通用 Swift 包装器中很难安全地维护,因此无论如何允许复制可能是更好的方法 - 例如,CFString 假设传入的内存永远不会被修改,对于C++字符串当然不一定如此。)