Google Protocol Buffer(简称Protobuf)是由Google推出的一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或RPC数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式
Protobuf的安装
这里推荐使用homebrew安装,如果没有安装,先安装吧
安装protobuf
1 |
|
安装Protobuf Compiler
1 |
|
Protobuf 使用
安装完成后我们就可以直接使用了!
注意
:上面只是安装了编译环境,可以帮我们将.proto文件编译成我们需要的.h和.m环境
Protobuf 导入
下面我们就可以新建一个iOS的工程了,然后使用pod方式管理protobuf
1 |
|
编写一个proto文件
这个proto文件就相当于一个类,不过要使用protobuf的语法来定义
可以简单的这么写:
1 |
|
注意:使用proto3的时候不需要在变量前面加上required和optional,默认就是optional
具体的语法规则我们后面介绍
编译这个proto文件
1 |
|
第一个号的地方是你.proto文件所在的位置,
第二个所在的地方是你编译完成之后.h和.m要导出的地方
将生成的文件添加到项目中
直接添加之后,编译一下,肯定是会报错的(除非你还活在远古的MRC)。这时候你需要将编译出来的对象文件标记为MRC
Target - BuildPhases - Compile Sources
然后我们仔细阅读,生成的.h文件会发现中的这一段:
因为我们是使用pod管理的因此我们这里使用<>导入,因此在
Target - Build Setting - Preprocessor Macros
在Debug和Release中都添加
这样我们在编译一下,Done! 这就没什么问题了!
到目前为止,我们基本上已经完成了Protobuf的所有配置,下面开始使用Protobuf了。
简单的使用
先导入我们生成的.h文件
#import "ProtobufChatmessage.pbobjc.h"
简单的创建一个的对象–序列化成data然后在解析这个data。
1 |
|
运行结果:
1 |
|
至此,我们已经成功的使用到了Protobuf,最直观的感觉就是直接从二进制的NSData转变成了我们想要的模型 Cool!!
Protocol还有很多高深的内容,网上也有很多关于他的文章,喜欢的可以多看看!
Protobuf 基本语法
字段格式定义
在Protobuf中,协议是由一系列的消息组成的。因此最重要的就是定义通信时使用到的消息格式。协议中个消息格式固定了t通信双方才能理解对象的码流。
限定修饰符① | 数据类型② | 字段名称③ | = | 字段编码值④ | [字段默认值⑤]
限定修饰符
限定修饰符 主要有required\optional\repeated(required在3.0的时候已经被废除,默认是optional)
Optional:表示是一个可选字段,可选对于发送方,在发送消息时,可以有选择性的设置或者不设置该字段的值。对于接收方,如果能够识别可选字段就进行相应的处理,如果无法识别,则忽略该字段,消息中的其它字段正常处理。因为optional字段的特性,很多接口在升级版本中都把后来添加的字段都统一的设置为optional字段,这样老的版本无需升级程序也可以正常的与新的软件进行通信,只不过新的字段无法识别而已,因为并不是每个节点都需要新的功能,因此可以做到按需升级和平滑过渡。
Repeated:表示该字段可以包含0~N个元素。其特性和optional一样,但是每一次可以包含多个值。可以看作是在传递一个数组的值
数据类型
Protobuf到C++的类型映射
proto Type | C++ Type | Notes |
---|---|---|
double | double | |
float | float | |
int32 | int32 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint32。 |
int64 | int64 | 使用可变长编码方式。编码负数时不够高效——如果你的字段可能含有负数,那么请使用sint64 |
uint32 | uint32 | Uses variable-length encoding |
uint64 | uint64 | Uses variable-length encoding. |
sint32 | int32 | 使用可变长编码方式。有符号的整型值。编码时比通常的int32高效。 |
sint64 | int64 | 使用可变长编码方式。有符号的整型值。编码时比通常的int64高效。 |
fixed32 | uint32 | 总是4个字节。如果数值总是比总是比228大的话,这个类型会比uint32高效 |
fixed64 | uint64 | 总是8个字节。如果数值总是比总是比256大的话,这个类型会比uint64高效。 |
sfixed32 | int32 | 总是4个字节 |
sfixed64 | int64 | 总是8个字节 |
bool | bool | |
string | string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 |
bytes | string | 可能包含任意顺序的字节数据。 |
字段名称
protobuf建议字段的命名采用以下划线分割的驼峰式。例如 first_name 而不是firstName.
字段编码值
有了该值,通信双方才能互相识别对方的字段。当然相同的编码值,其限定修饰符和数据类型必须相同。
编码值的取值范围为 1~2^32(4294967296)。
其中 1~15的编码时间和空间效率都是最高的,编码值越大,其编码的时间和空间效率就越低(相对于1-15),当然一般情况下相邻的2个值编码效率的是相同的,除非2个值恰好实在4字节,12字节,20字节等的临界区。比如15和16.
1900~2000编码值为Google protobuf 系统内部保留值,建议不要在自己的项目中使用。
protobuf 还建议把经常要传递的值把其字段编码设置为1-15之间的值。
消息中的字段的编码值无需连续,只要是合法的,并且不能在同一个消息中有字段包含相同的编码值
关于import
Protobuf接口文件可以像C语言的h文件一个,分离为多个,在需要的时候通过import导入需要对文件。其行为和C语言的#include或者iOS中的的#import的行为大致相同。
关于package
避免名称冲突,可以给每个文件指定一个package名称,对于java解析为java中的包。对于C++则解析为名称空间。
关于message
支持嵌套消息,消息可以包含另一个消息作为其字段。也可以在消息内定义一个新的消息。
关于enum
枚举的定义和C++相同,但是有一些限制。枚举值必须大于等于0的整数。使用分号(;)分隔枚举变量而不是C++语言中的逗号(,)
优缺点
优点
1 |
|
缺点
1 |
|
本文的Demo,放在这里