NSString initWithBytesNoCopy returns nil while using NSUTF32StringEncoding encoding

158 Views Asked by At

I am developing a bridge between C++ and Swift. So I need to convert C++ u32 string into swift String. Here is the problematic code:

    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];
    }

While debugging I found out, that objCline is nil. What am doing wrong?

Here is how I debug this: enter image description here

1

There are 1 best solutions below

2
pmdj On

Is your string data accepted if you use the initWithBytes: variant of the method?

The reason for the failure is either that you're supplying string data that CFString does not like (in which case the above copying variant would also fail), or that CFString simply does not support UTF-32 as an internal representation, so they must always be copied. I suspect the latter.

If you feel like finding out for sure, you can trawl through the source for an older version of the CFStringCreateWithBytesNoCopy() function (which is what the NSString ultimately calls down to) here:

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

It calls down to the rather long __CFStringCreateImmutableFunnel3() function, which will take a while to unpick. You could compile your own version of that function and step through it in the debugger to find out which condition is causing the return NULL in your case.

(As an aside, I suspect the …NoCopy semantics will be difficult to uphold safely in a general purpose Swift wrapper, so allowing the copy might be the better approach anyway - for example, CFString assumes the memory that is passed in will never be modified, which of course is not necessarily true for a C++ string.)