美文网首页Protobuf 集成配置ios基础iOS架构知识点
iOS 使用ProtocolBuffer进行数据传输

iOS 使用ProtocolBuffer进行数据传输

作者: 忠橙_g | 来源:发表于2018-04-02 19:26 被阅读445次

    什么是Protocol Buffer,这里就不多做介绍了,有兴趣自行百度即可。

    什么时候使用

    PB相比JSON和XML虽然有流量和转换效率方面的优势,但使用起来并没有JSON和XML方便,或者说,对于如何具体在实际项目中替换JSON和XML,相关文章较少(基本上都是到生成.pb.h和.pb.m文件就结束了...)。

    环境安装

    1.安装Homebrew(相信使用过pod的老司机都已经安装了,这里略过)
    2.安装protobuf编译所需工具

    brew install automake
    brew install libtool
    brew install protobuf
    

    如果已有automake,但不是最新版本,使用update进行更新:

    brew update automake
    

    添加依赖库

    PB的git托管地址为https://github.com/google/protobuf
    使用pod可以直接添加:

    pod 'Protobuf'
    

    使用PB创建Model

    1.创建.proto文件

    proto3语法可以参考Protocol Buffers 3.0 技术手册这篇文章。
    下面我们创建一个简单的DemoMessage.proto文件:
    使用Xcode新建Empty文件,命名为DemoMessage.proto,然后文件内容如下:

    syntax = "proto3";
    
    message DemoMessage {
        string query = 1;
        int32 page_number = 2;
        int32 result_per_page = 3;
    }
    
    2.生成对应的iOS文件

    在终端中,cd到DemoMessage.proto所在文件夹,然后使用如下命令:

    // OC
    protoc DemoMessage.proto --objc_out="./"
    
    // Swift
    protoc DemoMessage.proto --swift_out="./"
    

    就会在这个文件夹下生成对应的.m和.h文件了,然后添加到项目中就可以使用了。需要注意的是,OC生成的文件是不支持ARC的,需要在Compile Sources中添加-fno-objc-arc(这点让我有些困扰,如果有谁知道怎么能够生成arc的代码,留言请告诉我)。

    3.使用方法

    打开步骤2生成的文件,可以看到它继承于GPBMessage,并且生成了步骤1中定义的属性,以及对应的descriptor方法,你可以像正常的Model一样使用它:

    #import "DemoMessage.pbobjc.h"
    //...
    DemoMessage *message = [DemoMessage new];
    message.query = @"query";
    NSLog(@"%@",message.query);
    

    然后打开GPBMessage,看看里面有那些方法供我们使用:

    /**
    类方法:
     **/
    /// 返回一个autoreleased的对象
    + (instancetype)message;
    
    /// 通过解析提供的data数据创建一个新实例。如果发生错误,则该方法返回nil,并在errorPtr中返回错误。
    + (instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr;
    /// 同上,extensionRegistry是用于查找扩展的扩展注册表。
    + (instancetype)parseFromData:(NSData *)data
                         extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry
                                     error:(NSError **)errorPtr;
    
    /// 根据传入的GPBCodedInputStream数据新建一个实例,其他同上
    + (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
                                     extensionRegistry:
                                         (nullable GPBExtensionRegistry *)extensionRegistry
                                                 error:(NSError **)errorPtr;
    + (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
                                              extensionRegistry:
                                                  (nullable GPBExtensionRegistry *)extensionRegistry
                                                          error:(NSError **)errorPtr;
    /// 返回类的描述器
    + (GPBDescriptor *)descriptor;
    
    /**
    对象方法:
     **/
    /// 根据Data初始化
    - (instancetype)initWithData:(NSData *)data
                        extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry
                                    error:(NSError **)errorPtr;
    /// 根据GPBCodedInputStream初始化
    - (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
                                    extensionRegistry:
                                        (nullable GPBExtensionRegistry *)extensionRegistry
                                                error:(NSError **)errorPtr;
    
    /// 解析data数据并合并到当前对象中
    - (void)mergeFromData:(NSData *)data
        extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry;
    
    /// 将相同类型的对象的字段合并到当前对象
    - (void)mergeFrom:(GPBMessage *)other;
    
    /// 将当前对象写入GPBCodedOutputStream中
    - (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
    /// 将当前对象写入NSOutputStream中,并以制表符为分割
    - (void)writeDelimitedToOutputStream:(NSOutputStream *)output;
    
    /// 将当前对象序列化为NSData
    - (nullable NSData *)data;
    /// 将当前对象序列化为NSData,并以制表符为分割
    - (NSData *)delimitedData;
    
    /// ,
    /**
     * 获取序列化后对象的大小, 注意并不是文件的大小。
     * 下面这样拼接头文件是有误的:
     *
     * size_t size = [aMsg serializedSize];
     * NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
     * [foo writeSize:size];
     * [foo appendData:[aMsg data]];
     *
     * 应该使用:
     *
     * NSData *data = [aMsg data];
     * NSUInteger size = [aMsg length];
     * NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
     * [foo writeSize:size];
     * [foo appendData:data];
     *
     * @return 二进制表示中对象的大小。
     **/
    - (size_t)serializedSize;
    
    /// 获取对象的描述器(该方法的实现中设置各属性的索引)
    - (GPBDescriptor *)descriptor;
    
    /// 获取包含当前对象中设置的索引描述符的数组。
    - (NSArray *)extensionsCurrentlySet;
    
    /// 检查对象中是否存在与给定索引描述符匹配的索引集。
    - (BOOL)hasExtension:(GPBExtensionDescriptor *)extension;
    
    /// 获取此对象的给定索引的值。
    - (id)getExtension:(GPBExtensionDescriptor *)extension;
    
    /// 设置此对象的给定索引的值。
    - (void)setExtension:(GPBExtensionDescriptor *)extension
                   value:(nullable id)value;
    
    /// 为对象添加索引
    - (void)addExtension:(GPBExtensionDescriptor *)extension value:(id)value;
    
    /// 将给定索引的值替换为此消息的扩展的给定值。 这仅适用于重复的字段索引。 如果该字段是重复的POD类型,则该值应该是NSNumber。
    - (void)setExtension:(GPBExtensionDescriptor *)extension
                   index:(NSUInteger)index
                   value:(id)value;
    
    /// 删除对应索引
    - (void)clearExtension:(GPBExtensionDescriptor *)extension;
    
    /// 将此对象的所有字段重置为默认值。
    - (void)clear;
    

    看完上面的方法,对其结构就有了比较清楚的了解。

    4.与服务端的通信

    要使用ProtocolBuffer与服务器通信,可以使用正常的HTTP请求方式。

    接收数据

    服务端返回ProtocolBuffer序列化后的Data,直接使用parseFromData:error:方法转换成对应的对象即可(需要注意的是,服务端与客户端最好是使用同一个版本.proto文件生成的文件来进行序列化与反序列化)

    传参

    使用ProtocolBuffer与服务器的交互,有以下几种方案:

    • 直接使用正常的POST操作,向服务器传参,这样只是返回值是ProtocolBuffer,好处是可以直接使用原来的网络请求框架,基本上不需要修改请求的代码。
    • 将对象转成NSData,然后使用直接将其作为parameters参数,这种方案需要稍微修改请求的代码,改动不大,但能节省发送请求是的少许流量。

    使用gRPC

    gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具。
    如果是项目已决定用PB当作序列化,那么使用gRPC提供的一整套方案将会是一个很方便的选择。
    具体的使用教程可以参考下面的文章:
    gRPC初体验
    gRPC 官方文档中文版

    相关文章

      网友评论

        本文标题:iOS 使用ProtocolBuffer进行数据传输

        本文链接:https://www.haomeiwen.com/subject/eujxhftx.html