データシリアライズはいろんなケースで必要とされます。オブジェクトを一時的に保存、復元したり、ネットワークを通じて送受信したりするときなどです。JavaScriptのJSONなどはJava Script のObjectを記述したソースコード(テキストデータ)をそのままリクエスト・レスポンスで扱うので、目でみてとてもわかりやすい例です。最近では、MessagePackというJSONライクなバイナリシリアライゼーションがホットです。これはまたの機会に取り上げたいと思います。
今日は、Objective-Cを使ったシリアライゼーションをテストしてみました。
なぜ、ObjC? というと、最近、Objective-C / Linux を再評価しているからです。
環境 : GNUStep / Ubuntu 14.04
NSProxy / NSConnection
http://decode.red/net/archives/117
この記事と同じ環境を使っています。Objective-Cといえば、Mac, iPhoneというイメージが強いですが、もともとはNeXTSTEPのもので、ダイナミックな特徴がかなり強力です。これがLinuxで使えるので利用価値があると思いました。またObjective-CスキルがSwiftによって無駄になるということもなくなりますね。(?)
Person.h (シリアライズするデータはNSCoding Protocolを実装する必要があります。)
#import <Foundation/Foundation.h>
@interface Person : NSObject<NSCoding>
{
NSString *name;
NSInteger number;
}
@property (nonatomic, assign) NSString *name;
@property (nonatomic, assign) NSInteger number;
@end
MyData.h (Personオブジェクトを格納する配列)
#import <Foundation/Foundation.h>
@interface MyData : NSObject<NSCoding>
{
NSArray *person;
}
@property (nonatomic, assign) NSArray *person;
@end
Person.m
#import "Person.h"
@implementation Person
@synthesize name, number;
- (void)encodeWithCoder:(NSCoder *)code
{
[code encodeObject:self.name forKey:@"name"];
[code encodeInteger:self.number forKey:@"number"];
}
- (id)initWithCoder:(NSCoder *)decode
{
self = [super init];
if (self) {
self.name =
[decode decodeObjectForKey:@"name"];
self.number =
[decode decodeIntegerForKey:@"number"];
}
return self;
}
@end
MyData.m
#import "MyData.h"
@implementation MyData
@synthesize person;
- (void)encodeWithCoder:(NSCoder *)code
{
[code encodeObject:self.person forKey:@"person"];
}
- (id)initWithCoder:(NSCoder *)decode
{
self = [super init];
if (self) {
self.person =
[decode decodeObjectForKey:@"person"];
}
return self;
}
@end
SerWrite.m (シリアライズしてファイルに保存)
#import <Foundation/Foundation.h>
#import "Person.h"
#import "MyData.h"
//static NSString *path = @"/dev/stdout";
static NSString *path = @"./store.data";
@interface SerWrite : NSObject
@end
@implementation SerWrite
- (void) saveData
{
NSMutableArray *dataArray = [NSMutableArray array];
int i;
NSString *names[] = {@"Steve", @"Bill", @"Larry", @"Mark", @"Elon"};
for (i = 0; i < 5; i++) {
Person *data = [[Person alloc] init];
data.name = [NSString stringWithFormat:@"%@", names[i]];
data.number = i;
[dataArray addObject:data];
}
MyData *my = [[MyData alloc] init];
my.person = dataArray;
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[encoder encodeObject:my forKey:@"my"];
[encoder finishEncoding];
[data writeToFile:path atomically:YES];
//[data writeToFile:path atomically:NO];
}
@end
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SerWrite *ser = [[SerWrite alloc] init];
[ser saveData];
[pool drain];
return 0;
}
[/C]
SerRead.m (ファイルから読み込み、デシリアライズして表示)
[C]
#import <Foundation/Foundation.h>
#import "Person.h"
#import "MyData.h"
static NSString *path = @"./store.data";
@interface SerRead : NSObject
@end
@implementation SerRead
- (void)loadData
{
NSMutableData *data = [NSMutableData dataWithContentsOfFile:path];
//NSFileHandle *stdIn = [NSFileHandle fileHandleWithStandardInput];
//NSMutableData *data = [NSMutableData dataWithData:[stdIn readDataToEndOfFile]];
NSKeyedUnarchiver *decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
MyData *my = [decoder decodeObjectForKey:@"my"];
[decoder finishDecoding];
NSLog(@"%@", my.person);
for(Person *o in my.person){
NSLog(@"(%d) name: %@ ",[o number] + 1, [o name]);
}
}
@end
int main(int argc, char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
SerRead *ser = [[SerRead alloc] init];
[ser loadData];
[pool drain];
return 0;
}
コンパイル
gcc
gnustep-config --objc-flagsSerWrite.m Person.m MyData.m -o SerWrite -lobjc -lgnustep-base
gccgnustep-config --objc-flagsSerRead.m Person.m MyData.m -o SerRead -lobjc -lgnustep-base
さらに進んで、上記ソースのコメント部分を入れ替えて、ファイル入出力でなく標準入出力を採用すると、以下のようなコマンドでリダイレクトできます。
./SerWrite | ./SerRead
これはオブジェクトのパイプとなり、きちんと仕様をつくればWindowsのPowerShellもどきのようなことができます。xinetd(スーパーサーバ)を使って、ネットワークごしのパイプ接続をしても面白いかもしれません。

