Sending and receive NSMutableArray as Bytes using

2019-06-10 00:20发布

问题:

I am trying to send an Array from one player to another.

I create a struct in the header

typedef struct {
    Message message;
    unsigned int numBytes;
    void * bytes;
} MessagePingBackToCaller;

typedef struct {
    MessageType messageType;
} Message;

And try to send with this:

-(void)sendOpponentRadar{

    MessagePingBackToCaller sendMessage;
    sendMessage.message.messageType = kMessageTypeRecievedRadarPing;
    NSData *pingData = [NSKeyedArchiver archivedDataWithRootObject:[cover gatherAndReturnColumnNumbers]];

    sendMessage.bytes = pingData;

    NSData *data = [NSData dataWithBytes:&sendMessage length:sizeof(MessagePingBackToCaller)];    
    [self sendData:data];
}

And receive with this:

if (message->messageType == kMessageTypeRecievedRadarPing){

        MessagePingBackToCaller *messageSent = (MessagePingBackToCaller *)[data bytes];

        NSLog(@"Data : %@", messageSent->bytes);

        NSMutableArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:messageSent->bytes];
        NSLog(@"Results : %@", array);
    }

However no luck, so I went looking around and found that @rwenderlich wrote a little helpful bit:

  1. Convert NSArray to NSData

  2. In struct, store an unsigned int numBytes, and a void * bytes

  3. In numBytes, write the length of the NSData, and then write out the contents of the NSData

  4. On the other side, read numBytes to see how many bytes to read next, then read in that amount of bytes and reverse the process (bytes->NSData->NSArray).

Im pretty good on 1 and 2... I believe, but i get lost on 3 and 4. Could someone please help me translate this into meaningful code?

Thanks!

回答1:

You can just use NSKeyedArchiver to do the trick, dont use the c struct, make it an objective-c class, get data from the thing using NSKeyedArchiver and send that, then just unarchive in the other side, heres an example for an arbitrary class

interface

@interface GameState : NSObject
{
   //enum for session state
    SessionState _currentState;
    NSData *_data;
    NSString *_message;

    NSDate *_timestamp;

}

@property(nonatomic,retain) NSDate *timestamp;

@property(assign) SessionState currentState;
@property(nonatomic,retain) NSData *data;
@property(nonatomic,retain) NSString *message;
- (id)initWithCoder:(NSCoder *)coder;

implementation

@implementation GameState
@synthesize message=_message ,  currentState= _currentState, data=_data;
@synthesize  timestamp=_timestamp;
- (id)init
{
    self = [super init];
    if (self) {
        // Initialization code here.
        _killPlayback=FALSE;
        _lastPacket=FALSE;
        _number=0.0;
        _pass=FALSE;
        _timestamp=NULL;

    }

    return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
    [coder encodeObject:_timestamp forKey:@"Timestamp"];

    [coder encodeObject:_message forKey:@"Message"];
    [coder encodeObject:_data forKey:@"Data"];
    [coder encodeInt:_currentState forKey:@"State"];



}
// Decode an object from an archive
- (id)initWithCoder:(NSCoder *)coder
{
    self = [super init];
    _message  = [[coder decodeObjectForKey:@"Message"] retain];
    _data  = [[coder decodeObjectForKey:@"Data"] retain];
    _currentState=(SessionState)[coder decodeIntForKey:@"State"];
    _timestamp=[[coder decodeObjectForKey:@"Timestamp"] retain];
    return self;
}
@end

now you can get data from the object like

-(NSData*)dataFromObject:(id)obj
{
    return [NSKeyedArchiver archivedDataWithRootObject:obj];
}

and an object from the data

-(id)objectFromData:(NSData*)data
{
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}

and your workflow becomes on the sender side

GameState *state=..;
NSData* data=[self dataFromObject:state];
//send the data

and on the receiving side

GameState *state=[self objectFromData:data];

hope it helps



回答2:

You might also try the following to get compressed data (about 1/5 the size of the original):

-(NSData*)compressedMatchDataFromMatchState:(NSMutableDictionary*)matchState
{
    NSError* error = nil;
    NSData* compressedMatchData = [NSPropertyListSerialization dataWithPropertyList:matchState format:NSPropertyListBinaryFormat_v1_0 options:0 error:&error]; 
   return compressedMatchData;
 }

-(NSMutableDictionary*)matchStateFromCompressedMatchData:(NSData*)compressedMatchData
{
    NSError* error = nil;
    NSPropertyListFormat plf = NSPropertyListMutableContainersAndLeaves;
    NSMutableDictionary* matchState = [NSPropertyListSerialization propertyListWithData:compressedMatchData options:NSPropertyListMutableContainersAndLeaves format:&plf error:&error];
    return matchState;
 }