-->

Archive/Serialize Objective-C Object with C++ Inst

2019-08-14 15:05发布

问题:

I am using OpenCV static library in Objective-C to do some image processing, and although my app is working quite well, on the device itself it is rather slow. A large chunk of the processing can actually done beforehand, so I have decided I will serialize this data and just load that up when the app starts instead.

The data I need to serialize/archive is in an object of type CvSeq (openCV sequence - a pointer to a sequence of values). I basically want to save this to file so that it can be loaded up later. I thought I could do this by making a class that adhered to the NSCoding protocol and encode/decode from there:

@implementation MyObject

@synthesize point = _point;
@synthesize description = _description;

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        self.point = [decoder decodeObjectForKey:@"point"];
        self.description = [decoder decodeObjectForKey:@"description"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.point forKey:@"point"];
    [encoder encodeObject:self.description forKey:@"description"];
}

@end

But on the decodeObjectForKey: and encodeObject: calls I get the error

error: cannot convert 'objc_object*' to 'CvSeq*' in argument passing

Is there a problem with my code, or do I need to take another route to be able to achieve the same thing with non-objective-C instance variables being in my objects?

回答1:

Objective-C's serialization code is not going to know how to archive your C++ classes. You're going to need to explicitly write code to do that. Assuming "description" is the CvSeq* in the above code, you will need to write methods that convert from CvSeq to something that Cocoa knows how to archive. An NSString is probably the easiest place to start, even if it's not the most efficient scheme.

NSString* NSStringFromCvSeq(CvSeq* cppObj)
{
    // You have to write this, but at it's simplest it might be something like...
    return [NSString stringWithFormat: @"%d|%d|%d", cppObj->foo, cppObj->bar, cppObj->baz];
}

CvSeq* NewCvSeqFromNSString(NSString* encodedString)
{
    // You have to write this, but at it's simplest it might be something like...
    NSScanner* scanner = [NSScanner scannerWithString: encodedString];
    [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString: @"|"]];
    int foo = [scanner scanInt];
    int bar = [scanner scanInt];
    int baz = [scanner scanInt];

    return new CvSeq(foo, bar, baz);
}

- (id)initWithCoder:(NSCoder *)decoder {
    if (self = [super init]) {
        self.point = [decoder decodeObjectForKey:@"point"];
        self.description = NewCvSeqFromNSString([decoder decodeObjectForKey:@"description"]);
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.point forKey:@"point"];
    [encoder encodeObject: NSStringFromCvSeq(self.description) forKey:@"description"];
}

The key takeaway here is that Cocoa has no idea how to archive arbitrary types (excepting Objective-C types that already adopt NSCoding), and will never archive a C++ object "for free" without some sort of glue layer, since a C++ object has no way of adopting the NSCoding protocol.

Hope that helps!



回答2:

Instead of own encoding/serialization, NSData class with dataWithBytes method might help you to treat the OpenCV Mat data in Objective-C world as below.

NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize() * cvMat.total()];

After transfered into NSData class, we can store it to file.