Google Protocol Buffers总结-基础篇
转载请注明出处:http://elijahdou.github.io/
以前的公司主要是用protobuf做协议和数据传输,现在公司不用这个,为了以后方便使用,把以前的使用经验总结一下,方便以后重拾回来。
protobuf
Protocol Buffers简称protobuf或者pb,是由Google开发维护的夸语言、跨平台的 可扩展的结构化数据串行机制,官方地址Protocol Buffers。
现在常用的网络数据传输的结构化方式有:XML、JSON和Protobuf等。有很多篇介绍他们的对比和不同:blogprotobuf,json,xml,binary,Thrift之间的对比 使用 Protocol Buffers 代替 JSON 的五个原因。网上还有很多其他对比的帖子。不过简言之,protobuf的优缺点如下:
- 效率高:用protobuf序列化后的数据大小是json的10+分之一,xml格式的20+分之一,是二进制序列化的10+分之一。protobuf虽然会需要一定的本地编解码成本,但是数据压缩体积变小,网络传输速度加快,在效率和体验上的提升非常可观。
- 语法简答易学:保证做一次就会。
- 夸语言、跨平台、可扩展:不收平台的限制,并且无偿向后兼容。
- 缺点:唯一的缺点就是可读性差,因为数据都是编码后的,不像XML和JSON可读。
安装
Google的官方版本,只提供了JAVA C++ C 和 Python版本的pb,但是在github的开源项目上有OC和最新的SWIFT语言的版本。git还在维护的版本有qzix/protobuf-objc 和 alexeyxo/protobuf-objc,其中后者还支持swift,推荐使用后者,同时安装步骤也在该页。借助homebrew安装更方便,如果已安转homebrew请跳过第一步,摘录如下:
操作前请先获取系统权限,sudo一下
ruby -e "$(curl -fsSL https://raw.github.com/Homebrew/homebrew/go/install)"
brew install automake
brew install libtool
brew install protobuf
ln -s /usr/local/Cellar/protobuf/2.6.0/bin/protoc /usr/local/bin (optional)
git clone git@github.com:alexeyxo/protobuf-objc.git
./build.sh
Add /src/runtime/ProtocolBuffers.xcodeproj in your project.
使用-编写.proto
源文件
使用pb的第一步就是编写.proto
源文件,选用自己顺手的任一款编辑器即可。对应OC中的类,protobuf
称为message
消息,你会发现其语法结构与常见语言类似,所以简单易学。举例代码入下:
.proto
源文件格式从上面的源码中可以猜测一个大概,举例并解析如下:
关于msgName
.proto
中可以有多个message,dateType也可以是自定义message,但是源文件最好与主message同名,即mainMsgName.proto
,否则在使用的时候会出现一些名称不一致的问题,虽然仍能影响使用,但是会增加使用难度。
tips:一些公共的协议类的.proto
源文件,可以独立成一个文件,在其他使用到的该消息的文件的开头位置用import引入即可,如import "myproject/CommonMessages.proto"
关于required/repeated/optional(限定符)
- 在每个消息中必须至少留有一个required类型的字段(在实际使用中,可以全部是optional的),这样的变量在相应类的初始化时必须初始化,否则报错。
- 每个消息中可以包含0个或多个optional类型的字段,从名字可以看出,可选的,可以不初始化或者不使用该字段。推荐尽可能使用optional限定符,即使协议文档规定为required,两者仍可以兼容,并且避免以后协议修改造成的重写和重新编译。
- repeated表示的字段可以包含0个或多个数据。需要说明的是,这一点有别于C++/Java中的数组,因为后两者中的数组必须包含至少一个元素,可以理解为repeated也是一种可选类型。
- 如果打算在原有消息协议中添加新的字段,同时还要保证老版本的程序能够正常读取或写入,那么对于新添加的字段必须是optional或repeated。道理非常简单,老版本程序无法读取或写入新增的required限定符的字段。
关于dataType(数据类型)
这个很简单,数据类型名字也很好记,有表格如下:
.proto Type | Notes | C++ Type | Java Type |
double | double | double | |
float | float | float | |
int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int |
int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long |
uint32 | Uses variable-length encoding. | uint32 | int |
uint64 | Uses variable-length encoding. | uint64 | long |
sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int |
sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long |
fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 228. | uint32 | int |
fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 256. | uint64 | long |
sfixed32 | Always four bytes. | int32 | int |
sfixed64 | Always eight bytes. | int64 | long |
bool | bool | boolean | |
string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String |
bytes | May contain any arbitrary sequence of bytes. | string | ByteString |
oc语言的数据类型参考C++ 版本即可,此外pb还支持枚举类型的数据定义,举例如下:
使用-编译.proto
源文件
完成源文件的编写之后,就是要编译该源文件,编译的环境和命令在安装时就已经配置好了,我们只需要调用指令编译即可。编译得到的结果对于OC来说会生成一对.h .m
文件,即一个类,类名即为.proto
文件的名字;对于swift语言因该是生成一个.swift
文件。编译指令以如下:
protoc .proto源文件路径 --objc_out="输出文件路径"
protoc .proto源文件路径 --swift_out="输出文件路径"
使用-项目中引用
将上一步生成的目标文件引入到项目中,就可以当做普通的类来使用了。
tips:不同的oc版本的pb的开源项目略有不同,不过大同小异,如类名的命名方式等,一个较大的区别是有的支持ARC,有的不支持ARC 需要禁用,所以推荐用cocopods引入。
实际使用举例,以前面的Person.proto为例,常规的使用步骤如下(当然要导入必要的头文件):
swift Demo
总结
这只是pb使用的基础用法,看源码能得到很多收获。pb的精髓在于其编码方式,有兴趣的可以看一下。这一篇只是讲了pb的基本使用,再其使用过程中,还有很多需要注意的点,这一篇中就不讲了。
补充延伸阅读