Storing and Restoring std::vector from NSData

2019-05-30 11:18发布

问题:

I am attempting to store std::vector to NSData and back directly. My first attempt I converted each point to an NSValue and stored them with NSKeyedUnarchiver which seems terribly inefficient. My test dataset required 64MB of human readable text (with NSKeyedUnarchiver), versus converting each std:vector to NSData the resulting stored files is a much more reasonable 896kb. I am doing as follows to store the data:

    typedef std::vector<CGPoint> CGContour;
    typedef std::vector<std::vector<CGPoint>> CGContours;
    static CGContours contoursVector;

    contoursVector = CGContours(1024); //Populated with CGContours that are populated with CGPoints datatypes above

    //doing the following in a for loop, just showing record 0 for brevity
    NSData *contourData([[NSData alloc]
                       initWithBytesNoCopy: contoursVector[0].data()
                       length: contoursVector[0].size()
                       freeWhenDone:false]);

I am able to retrieve the buffer:

    const void *buffer = [contourData bytes];
    size_t len = [contourData length];

However, I am unable to figure out how to populate a std::vector with the const void buffer pointer. I have tried using every possible pointer and dereference combination I can think of - the only thing that I can get to compile is this:

   contoursVector[0] = *(CGContour *)[contourData bytes];

If I inspect the vector of CGPoints they are 0,0, so clearly something is not right.

EDIT: after implementing the suggested answer, sometimes it works, other times I get EXC_BAD_ACCESS. Here is the relevant back trace:

* thread #17: tid = 0x11bf7d4, 0x0000000111607551 libsystem_platform.dylib`_platform_memmove$VARIANT$Ivybridge + 49, queue = 'NSOperationQueue 0x7fa298f51000 :: NSOperation 0x7fa29f3251f0 (QOS: UTILITY)', stop reason = EXC_BAD_ACCESS (code=1, address=0x126e27000)
frame #0: 0x0000000111607551 libsystem_platform.dylib`_platform_memmove$VARIANT$Ivybridge + 49
frame #1: 0x000000010d01890f Foundation`NSCopyMemoryPages + 57
frame #2: 0x000000010cf9b737 Foundation`_NSDataCreateVMDispatchData + 103
frame #3: 0x000000010cf99cf2 Foundation`-[_NSPlaceholderData initWithBytes:length:copy:deallocator:] + 230
frame #4: 0x000000010cfa5902 Foundation`-[NSData(NSData) initWithBytes:length:] + 37
* frame #5: 0x000000010cfeabfb Foundation`+[NSData(NSData) dataWithBytes:length:] + 54
frame #6: 0x000000010c5c998a TDTPhotoLib`storePointData() + 682 at TDTContourImage.mm:562

Odd thing is both the contours and the contour being converted to data both look valid in the debugger and the issue seems to be intermittent (sometimes it works other times it doesn't but can't tell what if anything may be different)

EDIT 2:

I am able to iterate over every point, but it crashes on the NSData line.

NSMutableArray<NSData *> *groupedPointsArrayMain = [NSMutableArray new];

for(const CGContour &contour : contoursVector)
{
    if (contour.size() > 0) {

        // I am able to iterate over every point and store them this way
        NSMutableArray *contourPoints = [NSMutableArray arrayWithCapacity:contour.size()];

        for(const CGPoint &point : contour)
        {
            [contourPoints addObject:[NSValue valueWithCGPoint:point]];
        }

        //When it crashes, it will crash on this line
        //despite it successfully walking over each point
        //in the code directly above
        NSData *data = [NSData dataWithBytes: contour.data()
                                      length: (contour.size() * cgContourSize)];

        [groupedPointsArrayMain addObject:data];
    }
}

回答1:

Something like this should do the trick. Please note I didn't try to compile this code since I'm on Linux atm.

typedef std::vector<CGPoint> CGContour;
typedef std::vector<CGContour> CGContours;

const size_t contourSize = sizeof(CGContour);

NSMutableArray<NSData *> *datas = [NSMutableArray new];

{ // store
  CGContours contours(1024);

  for(const CGContour &contour : contours)
  {
    NSData *data = [NSData dataWithBytes: contour.data()
                                  length: contour.size() * contourSize];
    [datas addObject:data]
  }
}

{ // restore
  CGContours contours;

  for(NSData *data in datas)
  {
    const size_t count = [data length] / contourSize;
    CGPoint *first = (CGPoint *)[data bytes];
    CGPoint *last = first + count;

    contours.emplace_back(first, last);
  }
}