How to convert NSValue to NSData and back?

后端 未结 3 1317
旧巷少年郎
旧巷少年郎 2020-12-30 07:45

A have a number of NSValue (obtained via KVC valueForKey) that I need to append to an NSData object in order to send it over the netwo

相关标签:
3条回答
  • 2020-12-30 08:27

    Martin Gordon's answer is getting close, but fortunately you don't have to manually parse the objCType string. There's a function that does that: NSGetSizeAndAlignment. From that you get the size/length of the value obtained from getValue:. This question lead me to the solution.

    I ended up creating a category for NSData that allows me to create NSData from NSNumber or NSValue objects:

    @interface NSData (GameKitCategory)
    +(NSData*) dataWithValue:(NSValue*)value;
    +(NSData*) dataWithNumber:(NSNumber*)number;
    @end
    
    @implementation NSData (GameKitCategory)
    +(NSData*) dataWithValue:(NSValue*)value
    {
        NSUInteger size;
        const char* encoding = [value objCType];
        NSGetSizeAndAlignment(encoding, &size, NULL);
    
        void* ptr = malloc(size);
        [value getValue:ptr];
        NSData* data = [NSData dataWithBytes:ptr length:size];
        free(ptr);
    
        return data;
    }
    
    +(NSData*) dataWithNumber:(NSNumber*)number
    {
        return [NSData dataWithValue:(NSValue*)number];
    }
    @end
    

    I also add a small header before this NSValue/NSNumber data that allows me to decode which property the data is for and how many bytes are in the data section. With that I can restore the value to the remote property.

    0 讨论(0)
  • 2020-12-30 08:35

    Since NSValue adopts the NSCoding protocol, you can use the NSCoder subclasses, NSKeyedArchiver and NSKeyedUnarchiver:

    - (NSData *)dataWithValue:(NSValue *)value {
      return [NSKeyedArchiver archivedDataWithRootObject:value];
    }
    
    - (NSValue *)valueWithData:(NSData *)data {
      return (NSValue *)[NSKeyedUnarchiver unarchiveObjectWithData:data];
    }
    

    If NSKeyedArchiver and NSKeyedUnarchiver can't be used (for resulting data size issues), then you would have to find the size of the contained value yourself:

    - (NSData *)dataWithValue:(NSValue *)value {
      // Can use NSGetSizeAndAlignment() instead. See LearnCocos2D's answer.
      if (type[0] == '{') {
        // Use the various NSValue struct value methods to detect the type.
      } else if (type[0] == 'c') {
        size = sizeof(char);
      } else if (type[0] == 'i') {
        size = sizeof(int);     
      } // etc for all/most of the values in the table linked below [1];
    
      void *bytes = malloc(size);
      [value getValue:bytes];
      return [NSData dataWithBytes:bytes length:size];
    }
    

    [1]: Objective-C Type Encodings

    0 讨论(0)
  • 2020-12-30 08:37

    You can use NSCoder (like NSKeyedArchiver) to archive the data, then send the resulting NSData. On the other side you can unarchive it. There may be some caveats with this (for example http://www.cocoabuilder.com/archive/cocoa/82344-nskeyedarchiver-and-nsvalue.html) esepcially if you are wrapping structs and other non-archiving stuff in NSValue.

    0 讨论(0)
提交回复
热议问题