Search code examples
iosobjective-cnsdata

NSData getBytes Randomly Returns Garbage


I'm trying to save and load a custom file format in an iOS app. The format stores various data, but among it is the name a user entered for a map. I found that at random, as I'm trying to read this map name (stored as chars in the file), my NSData object will read garbage. Here is my code for reading the map name (start is already set to the correct start location):

NSData* data = ....;
uint mapNameLength;
char* mapNameChar;
NSString* mapNameString;

[data getBytes:&mapNameLength range:NSMakeRange(start, 4)];
start += 4;
mapNameChar = (char *)malloc(sizeof(char) * mapNameLength);
[data getBytes:mapNameChar range:NSMakeRange(start, mapNameLength)];
mapNameString = [NSString stringWithUTF8String:mapNameChar];

NSLog(@"mapNameLength: %u, mapNameChar: %s, Map name string: %@", mapNameLength, mapNameChar, mapNameString);

As you can see, I am reading the length of the name, then reading that many char values, then converting it into an NSString. Here is the output of the NSLog when it works (I just hit the keyboard a bunch to make a long name):

mapNameLength: 49, mapNameChar: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdv, Map name string: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdv

Here is the output of the NSLog when it doesn't work:

mapNameLength: 49, mapNameChar: kandfianeifniwbvuandivbwirngiwnrivnwrivnwidnviwdvfi?p?, Map name string: (null)

Those "?" are actually special characters that don't display here, so I added them. The first is "ETX" and the second is "SOH" in Sublime Text.

To create the file, this is what I am doing:

NSString* mapName = ....;
uint mapNameLength = (uint)mapName.length;
NSMutableData* data = ....;
//...
//Write file type and version here
//...
[data appendBytes:&mapNameLength length:4];
[data appendData:[mapName dataUsingEncoding:NSUTF8StringEncoding]];
//...
//Write other stuff
//...

NSString* path = [FileManager applicationDocumentsDirectory];
path = [path stringByAppendingFormat:@"/%@", fileName];
BOOL success = [data writeToFile:path options:NSDataWritingAtomic error:&error];

I've only written the file once, so I know the data is always the same. Why then, would my data object sometimes get random bytes from it?


Solution

  • I am not certain, however I don't think you are storing a terminating NUL character, so you need to add one to the buffer once you've read the string in:

    mapNameChar = (char *)malloc(sizeof(char) * (mapNameLength + 1));   // Add +1
    [data getBytes:mapNameChar range:NSMakeRange(start, mapNameLength)];
    mapNameChar[mapNameLength] = '\0';            // Terminate with NUL
    mapNameString = [NSString stringWithUTF8String:mapNameChar];
    

    Better still forget about malloc() and the C-String and create the NSString directly from the NSData object:

    mapNameString = [[NSString alloc] initWithBytes:(const char *)([data bytes]) + start
                                             length:mapNameLength
                                           encoding:NSUTF8StringEncoding];