分享一下用了两个月的ProtocolBuffer,对它的理解。
上班第一天,技术总监丢给我一个PortocolBuffer的文档,说这个是我们的网络协议文档,到时候你用这个和服务器交互?整个人都傻了,以前都没听过。不知道为什么要用ProtocolBuffer,用JSON不好吗? 哎,想太多也没用。作为小弟的只能学怎么去用。
那么ProtocolBuffer究竟是什么呢?我理解为就像<NSCoding>协议一样。可以把一个内存里的对象序列化成一个二进制的包。反过来可以把二进制的包反序列化成一个对象。
那么为什么要用ProtocolBuffer呢?看了一堆网上说的好处,我只记得一点,这个序列化成的二进制的包比其他传输格式小很多,可以节约网络流量。
那么重点是,怎么用ProtocolBuffer呢?这里就要详细点说了。
首先,ProtocolBuffer是Google的产物,官方说的是:"目前支持Java,Python, 和C++。在最新的版本,增加了Go,JavaNano,Ruby,C#语言,然后呢更多语言支持会出来"。 没错,目前2015年10月21日,官方原生不支持OC,或者Swift。那么做iOS的我们怎么办呢?
这里有两条思路。第一条思路是用C++的那一套,oc和C++非常亲和。第二条思路是用GitHub上面别人做好的OC,或者Swift那一套。可惜小弟我不熟悉C++,不过我还是把别人写好的C++的思路贴出来吧。
C++思路:https://developers.google.com/protocol-buffers/docs/cpptutorial 官方有详细的使用说明和教程。然后呢在我们的程序里面,OC和C++的桥接:
序列化成NSData:
- (NSData *)getDataForPacket:(Packet *)packet {
std::string ps = packet->SerializeAsString();
return [NSData dataWithBytes:ps.c_str() length:ps.size()];
反序列化成对象(注意这个Packet对象是我假定的,是proto文件里面编译出来的,如果你看不懂前面这句话,说明你没看官方说明,官方说明写得很详细)
- (Packet *)getPacketFromNSData:(NSData *)data {
char raw[[data length]];
Packet *p = new Packet;
[data getBytes:raw length:[data length]];
p->ParseFromArray(raw, [data length]);
return p;
}
好了,现在来说第二条思路。这条是我走过,走通过的路。
我也是用别人大牛做好的东西:https://github.com/alexeyxo/protobuf-objc 这个github里面有详细的使用说明。我简单的讲一下思路吧。
首先,需要搭建一个编译protocolbuffer的编译器:
1.检查你的mac有没有安装Homebrew :在命令行里面敲 brew -v
2.如果没有,就安装:ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
3.安装需要的小工具:brew install automake
brew install libtool
brew install protobuf
4.(可以跳过)为你的编译器创建一个连接 ln -s /usr/local/Cellar/protobuf/2.6.1/bin/protoc /usr/local/bin
5.把这个从网上克隆下来 git clone https://github.com/alexeyxo/protobuf-objc.git
6.编译 ./scripts/build.sh
这样编译proto文件的编译器就弄好了。需要编译proto文件的时候,命令行进入有proto文件的文件夹:protoc --plugin=/usr/local/bin/protoc-gen-objc person.proto --objc_out="./"
“person.proto”改成你的proto文件名字。这样就可以把一个proto文件编译成一个person.h 和person.m 这样我们就可以吧.h和.m放入我们的工程使用了。
如果你不知道什么是proto文件。那么我简单说一下,proto文件就是用于规定一个类,有那些属性,叫什么名称之类的。我这边是总监写的,因为他负责服务器的数据和交互接口。
其次,我们要用cocopod导入 pod'ProtocolBuffers', '~> 1.9.8' 这样我们的工程就可以把.h和.m用于把类序列化成NSData和反序列化了。
这样就大功告成。
****使用ProtocolBuffer和服务器交互::
把一个对象发到服务器:
首先把这个对象设置好属性。
例如: PersonBuilder *personBuilder = [[PersonBuilder alloc] init];
personBuilder.age = 18;
personBuilder.name = @"xiaosan"
序列化:NSData *data = [PersonBuilder build].data;
接下来,怎么把NSData发送到服务器呢?
用NSURLSessionDataTask就可以了。这里需要注意一点就是 [requestsetValue: @"application/protobuf"forHTTPHeaderField:@"Content-Type"];
这里服务器一般会设置好这个Content-Type,标明是Protobuffer格式的。这个是自定义的。
这样就把一个参数为 18岁, 名字 xiaosan的对象发到服务器了。然后服务器根据逻辑会返回一个NSData。
我们再把这个NSData反序列化成我们的对象。
例如,假设返回的对象是Person :
NSURLSessionDataTask *task = [sessiondataTaskWithRequest:request completionHandler:^(NSData *data,NSURLResponse *response, NSError *error) {
Person *person = [Person parseFromData:data];
............
}];
写到这里,我会的都写出来了,protocolbuffer整条线也走完了。
我们总监在这里还弄了一个与众不同的地方,他在二进制包的前面拼了两个字节的正短整型的数,这样来区分不同的逻辑。比如 我发送103是表示登录,然后服务器就会把我上传的对象按照登录的对象,反序列化处理。如果我发送104是表示获取订单,服务器就按照获取订单的对象处理。 当然,这个只是总监想的小点子而已,不同的公司有不同的处理办法吧。
好了,写完了,我写的不一定对。