iOS 使用ProtocolBuffer進(jìn)行數(shù)據(jù)傳輸

什么是Protocol Buffer,這里就不多做介紹了驰吓,有興趣自行百度即可。

什么時(shí)候使用

PB相比JSON和XML雖然有流量和轉(zhuǎn)換效率方面的優(yōu)勢(shì),但使用起來(lái)并沒有JSON和XML方便辆布,或者說(shuō),對(duì)于如何具體在實(shí)際項(xiàng)目中替換JSON和XML茶鉴,相關(guān)文章較少(基本上都是到生成.pb.h和.pb.m文件就結(jié)束了...)锋玲。

環(huán)境安裝

1.安裝Homebrew(相信使用過pod的老司機(jī)都已經(jīng)安裝了,這里略過)
2.安裝protobuf編譯所需工具

brew install automake
brew install libtool
brew install protobuf

如果已有automake涵叮,但不是最新版本惭蹂,使用update進(jìn)行更新:

brew update automake

添加依賴庫(kù)

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

pod 'Protobuf'

使用PB創(chuàng)建Model

1.創(chuàng)建.proto文件

proto3語(yǔ)法可以參考Protocol Buffers 3.0 技術(shù)手冊(cè)這篇文章。
下面我們創(chuàng)建一個(gè)簡(jiǎn)單的DemoMessage.proto文件:
使用Xcode新建Empty文件割粮,命名為DemoMessage.proto盾碗,然后文件內(nèi)容如下:

syntax = "proto3";

message DemoMessage {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
}
2.生成對(duì)應(yīng)的iOS文件

在終端中,cd到DemoMessage.proto所在文件夾舀瓢,然后使用如下命令:

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

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

就會(huì)在這個(gè)文件夾下生成對(duì)應(yīng)的.m和.h文件了廷雅,然后添加到項(xiàng)目中就可以使用了。需要注意的是,OC生成的文件是不支持ARC的榜轿,需要在Compile Sources中添加-fno-objc-arc(這點(diǎn)讓我有些困擾幽歼,如果有誰(shuí)知道怎么能夠生成arc的代碼,留言請(qǐng)告訴我)谬盐。

3.使用方法

打開步驟2生成的文件甸私,可以看到它繼承于GPBMessage,并且生成了步驟1中定義的屬性飞傀,以及對(duì)應(yīng)的descriptor方法皇型,你可以像正常的Model一樣使用它:

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

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

/**
類方法:
 **/
/// 返回一個(gè)autoreleased的對(duì)象
+ (instancetype)message;

/// 通過解析提供的data數(shù)據(jù)創(chuàng)建一個(gè)新實(shí)例砸烦。如果發(fā)生錯(cuò)誤弃鸦,則該方法返回nil,并在errorPtr中返回錯(cuò)誤幢痘。
+ (instancetype)parseFromData:(NSData *)data error:(NSError **)errorPtr;
/// 同上唬格,extensionRegistry是用于查找擴(kuò)展的擴(kuò)展注冊(cè)表。
+ (instancetype)parseFromData:(NSData *)data
                     extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry
                                 error:(NSError **)errorPtr;

/// 根據(jù)傳入的GPBCodedInputStream數(shù)據(jù)新建一個(gè)實(shí)例颜说,其他同上
+ (instancetype)parseFromCodedInputStream:(GPBCodedInputStream *)input
                                 extensionRegistry:
                                     (nullable GPBExtensionRegistry *)extensionRegistry
                                             error:(NSError **)errorPtr;
+ (instancetype)parseDelimitedFromCodedInputStream:(GPBCodedInputStream *)input
                                          extensionRegistry:
                                              (nullable GPBExtensionRegistry *)extensionRegistry
                                                      error:(NSError **)errorPtr;
/// 返回類的描述器
+ (GPBDescriptor *)descriptor;

/**
對(duì)象方法:
 **/
/// 根據(jù)Data初始化
- (instancetype)initWithData:(NSData *)data
                    extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry
                                error:(NSError **)errorPtr;
/// 根據(jù)GPBCodedInputStream初始化
- (instancetype)initWithCodedInputStream:(GPBCodedInputStream *)input
                                extensionRegistry:
                                    (nullable GPBExtensionRegistry *)extensionRegistry
                                            error:(NSError **)errorPtr;

/// 解析data數(shù)據(jù)并合并到當(dāng)前對(duì)象中
- (void)mergeFromData:(NSData *)data
    extensionRegistry:(nullable GPBExtensionRegistry *)extensionRegistry;

/// 將相同類型的對(duì)象的字段合并到當(dāng)前對(duì)象
- (void)mergeFrom:(GPBMessage *)other;

/// 將當(dāng)前對(duì)象寫入GPBCodedOutputStream中
- (void)writeToCodedOutputStream:(GPBCodedOutputStream *)output;
/// 將當(dāng)前對(duì)象寫入NSOutputStream中购岗,并以制表符為分割
- (void)writeDelimitedToOutputStream:(NSOutputStream *)output;

/// 將當(dāng)前對(duì)象序列化為NSData
- (nullable NSData *)data;
/// 將當(dāng)前對(duì)象序列化為NSData,并以制表符為分割
- (NSData *)delimitedData;

/// 门粪,
/**
 * 獲取序列化后對(duì)象的大小喊积, 注意并不是文件的大小。
 * 下面這樣拼接頭文件是有誤的:
 *
 * size_t size = [aMsg serializedSize];
 * NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
 * [foo writeSize:size];
 * [foo appendData:[aMsg data]];
 *
 * 應(yīng)該使用:
 *
 * NSData *data = [aMsg data];
 * NSUInteger size = [aMsg length];
 * NSMutableData *foo = [NSMutableData dataWithCapacity:size + sizeof(size)];
 * [foo writeSize:size];
 * [foo appendData:data];
 *
 * @return 二進(jìn)制表示中對(duì)象的大小玄妈。
 **/
- (size_t)serializedSize;

/// 獲取對(duì)象的描述器(該方法的實(shí)現(xiàn)中設(shè)置各屬性的索引)
- (GPBDescriptor *)descriptor;

/// 獲取包含當(dāng)前對(duì)象中設(shè)置的索引描述符的數(shù)組乾吻。
- (NSArray *)extensionsCurrentlySet;

/// 檢查對(duì)象中是否存在與給定索引描述符匹配的索引集。
- (BOOL)hasExtension:(GPBExtensionDescriptor *)extension;

/// 獲取此對(duì)象的給定索引的值拟蜻。
- (id)getExtension:(GPBExtensionDescriptor *)extension;

/// 設(shè)置此對(duì)象的給定索引的值绎签。
- (void)setExtension:(GPBExtensionDescriptor *)extension
               value:(nullable id)value;

/// 為對(duì)象添加索引
- (void)addExtension:(GPBExtensionDescriptor *)extension value:(id)value;

/// 將給定索引的值替換為此消息的擴(kuò)展的給定值。 這僅適用于重復(fù)的字段索引瞭郑。 如果該字段是重復(fù)的POD類型辜御,則該值應(yīng)該是NSNumber鸭你。
- (void)setExtension:(GPBExtensionDescriptor *)extension
               index:(NSUInteger)index
               value:(id)value;

/// 刪除對(duì)應(yīng)索引
- (void)clearExtension:(GPBExtensionDescriptor *)extension;

/// 將此對(duì)象的所有字段重置為默認(rèn)值屈张。
- (void)clear;

看完上面的方法,對(duì)其結(jié)構(gòu)就有了比較清楚的了解袱巨。

4.與服務(wù)端的通信

要使用ProtocolBuffer與服務(wù)器通信阁谆,可以使用正常的HTTP請(qǐng)求方式。

接收數(shù)據(jù)

服務(wù)端返回ProtocolBuffer序列化后的Data愉老,直接使用parseFromData:error:方法轉(zhuǎn)換成對(duì)應(yīng)的對(duì)象即可(需要注意的是场绿,服務(wù)端與客戶端最好是使用同一個(gè)版本.proto文件生成的文件來(lái)進(jìn)行序列化與反序列化)

傳參

使用ProtocolBuffer與服務(wù)器的交互,有以下幾種方案:

  • 直接使用正常的POST操作嫉入,向服務(wù)器傳參焰盗,這樣只是返回值是ProtocolBuffer璧尸,好處是可以直接使用原來(lái)的網(wǎng)絡(luò)請(qǐng)求框架,基本上不需要修改請(qǐng)求的代碼熬拒。
  • 將對(duì)象轉(zhuǎn)成NSData爷光,然后使用直接將其作為parameters參數(shù),這種方案需要稍微修改請(qǐng)求的代碼,改動(dòng)不大澎粟,但能節(jié)省發(fā)送請(qǐng)求是的少許流量蛀序。

使用gRPC

gRPC是由Google主導(dǎo)開發(fā)的RPC框架,使用HTTP/2協(xié)議并用ProtoBuf作為序列化工具活烙。
如果是項(xiàng)目已決定用PB當(dāng)作序列化徐裸,那么使用gRPC提供的一整套方案將會(huì)是一個(gè)很方便的選擇。
具體的使用教程可以參考下面的文章:
gRPC初體驗(yàn)
gRPC 官方文檔中文版

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啸盏,一起剝皮案震驚了整個(gè)濱河市重贺,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌回懦,老刑警劉巖檬姥,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異粉怕,居然都是意外死亡健民,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門贫贝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)秉犹,“玉大人,你說(shuō)我怎么就攤上這事稚晚〕缍拢” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵客燕,是天一觀的道長(zhǎng)鸳劳。 經(jīng)常有香客問我,道長(zhǎng)也搓,這世上最難降的妖魔是什么赏廓? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮傍妒,結(jié)果婚禮上幔摸,老公的妹妹穿的比我還像新娘。我一直安慰自己颤练,他們只是感情好既忆,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般患雇。 火紅的嫁衣襯著肌膚如雪跃脊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天苛吱,我揣著相機(jī)與錄音匾乓,去河邊找鬼。 笑死又谋,一個(gè)胖子當(dāng)著我的面吹牛拼缝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彰亥,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼咧七,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了任斋?” 一聲冷哼從身側(cè)響起继阻,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎废酷,沒想到半個(gè)月后瘟檩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡澈蟆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年墨辛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趴俘。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡睹簇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寥闪,到底是詐尸還是另有隱情太惠,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布疲憋,位于F島的核電站凿渊,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏缚柳。R本人自食惡果不足惜埃脏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望喂击。 院中可真熱鬧剂癌,春花似錦淤翔、人聲如沸翰绊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)监嗜。三九已至谐檀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間裁奇,已是汗流浹背桐猬。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留刽肠,地道東北人溃肪。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像音五,于是被迫代替她去往敵國(guó)和親惫撰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容