日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Protocol Buffers 在 iOS 中的使用

發布時間:2025/5/22 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Protocol Buffers 在 iOS 中的使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

翻譯自:Introduction to Protocol Buffers on iOS

對大多數的應用來說,后臺服務、傳輸和存儲數據都是個重要的模塊。開發者在給一個 web service 寫接口時,通常使用 JSON 或者 XML 來發送和接收數據,然后根據這些數據生成結構并解析。

盡管有大量的 API 和框架幫助我們序列化和反序列化,來支持一些后臺接口開發的日常工作,比如說更新代碼或者解析器來支持后臺的模型變化。

但是如果你真的想提升你的新項目的健壯性的話 ,考慮下用?protocol buffers,它是由 Google 開發用來序列化數據結構的一種跨語言的方法。在很多情況下,它比傳統的 JSON 和 XML 更加靈活有效。其中一個關鍵的特點就是,你只需要在其支持的任何語言和編譯器下,定義一次數據結構——包括 Swift! 創建的類文件就可以很輕松的讀寫成對象。

在這篇教程中,會使用一個 Python 服務端與一個 iOS 程序交互。你會學到 protocol buffers 是如何工作,如何配置環境,最后怎樣使用 protocol buffers 傳輸數據。

怎么,還是不相信 protocol buffers 就是你所需要的東西?接著往下讀吧。

注意:這篇教程是基于你已經有了一定的 iOS 和 Swift 經驗,同時有一定的基本的服務端和 terminal 基礎。 同時,確保你使用的是蘋果的 Xcode 8.2或以后的版本.

##準備開始 RWCards這個APP可以用來查看你的會議門票和演講者名單。下載Starter Project并打開根目錄Starter。先熟悉一下這下面這三部分: #####The Client 在?Starter/RWCards下,打開?RWCards.xcworkspace,我們來看看這幾個主要的文件:

  • SpeakersListViewController.swift?管理了一個用來展示演講者名單的table view。這個控制器現在還只是個模板因為你還沒有為其創建模型。
  • SpeakersViewModel.swift?相當于 SpeakersListViewController 的數據源,它會包含有演講者的名單數據。
  • CardViewController.swift?用來展示參會者的名片和他的社交信息.
  • RWService.swift 管理客戶端和后端的交互。你可能會用到 Alamofire 來發起服務請求。
  • Main.storyboard?整個 APP 的 storyboard.

整個工程使用?CocoaPods?來拉取這兩個框架:

  • Swift Protobuf?支持在 Xcode 中使用 Protocol Buffers.
  • Alamofire?一個 HTTP 網絡庫,你會用到它來請求服務器。

注意:這篇教程中你會用到 Swift Protobuf 0.9.24 和 Google’s Protoc Compiler 3.1.0. 它們已經打包在項目里了,所以你不需要再做別的。

Protocol Buffers 是如何工作的?

開始使用 protocol buffers 前,首先要定義一個 .proto 文件。在這個文件中指定了你的數據結構信息。下面是一個 .proto 文件的示例:

syntax = "proto3";message Contact {enum ContactType {SPEAKER = 0;ATTENDANT = 1;VOLUNTEER = 2;}string first_name = 1;string last_name = 2;string twitter_name = 3;string email = 4;string github_link = 5;ContactType type = 6;string imageName = 7; }; 復制代碼

這個文件里定義了一個 Contact 的 message 和它的相關屬性。

.proto 文件定義好了后,你只需要把這個文件交給 protocol buffer 的編譯器,編譯器會用你選擇的語言創建好一個數據類(Swift 中的 結構)。你可以直接在項目中使用這個類/結構,非常簡單!

編譯器會將 .proto 中的 message 轉換成事先選擇的語言,并生成模型對象的源文件。后面會提到定義**.proto**信息的更多細節。 另外在考慮 protocol buffers 之前,你應該考慮它是不是你項目的最佳方案。

優勢

JSON 和 XML 可能是目前開發者們用來存儲和傳輸數據的標準方案,而 protocol buffers 與之相比有以下優勢:

  • 快速且小巧:按照 Google 所描述的,protocol buffers 的體積要小3-10倍,速度比XML要快20-100倍。可以在這篇文章?,它的作者是 Damien Bod,文中比較了一些主流文本格式的讀寫速度。
  • 類型安全:Protocol buffers 像 Swift 一樣是類型安全的,使用 protocol buffers 時 你需要指定每一個屬性的類型。
  • 自動反序列化:你不需要再去編寫任何的解析代碼,只需要更新 .proto 文件就行了。 file and regenerate the data access classes.
  • 分享就是關心:因為支持多種語言,因此可以在不同的平臺中共享數據模型,這意味著跨平臺的工作會更輕松。

局限性

Protocol buffers 雖然有著諸多優勢,但是它也不是萬能的:

  • 時間成本:在老項目中去使用 protocol buffers 可能會不太高效,因為需要轉換成本。同時,項目成員還需要去學習一種新的語法。
  • 可讀性:XML 和 JSON 的描述性更好,并且易于閱讀。Protocol buffers 的原數據無法閱讀,并且在沒有 .proto 文件的情況下沒辦法解析。
  • 僅僅是不適合而已:當你想要使用類似于XSLT這樣的樣式表時,XML是最好的選擇。所以 protocol buffers 并不總是最佳工具。
  • 不支持:編譯器可能不支持你正在進行中的項目所使用的語言和平臺。

盡管并不是適合于所有的情況,但 protocol buffers 確確實實有著很多的優勢。 把程序運行起來試試看吧。

不幸的是你現在還看不到任何信息,因為數據源還沒有初始化。你要做的是請求服務端并且將演講者和參會者數據填充到頁面上。首先,你會看到項目中提供的:

Protocol Buffer 模板

Head back to Finder and look inside?Starter/ProtoSchema. You’ll see the following files: 打開 Starter/ProtoSchema 目錄,你會看到這些文件:

  • contact.proto?用 protocol buffer 的語法定義了一個 contact 的結構。之后會更詳細地說明這個。
  • protoScript.sh?這個 bash 腳本使用 protocol buffer 的編譯器讀取 contact.proto 分別生成了 Swift 和 Python 的數據模型。
服務端

Starter/Server 目錄下包括:

  • RWServer.py 是放在Flask上的一個 Python 服務。包含兩個 GET 請求:

    • /currentUser 獲取當前參會者的信息。
    • /speakers 獲取演講者列表。
  • RWDict.py?包含了 RWServer 將要讀取的演講者列表數據.

現在是時候配置環境來運行 protocol buffers 了。在下面的章節中,你會創建好運行 Google 的 protocol buffer編譯器環境,Swift 的 Protobuf 插件,并安裝 Flask 來運行你的 Python 服務。

環境配置

在使用 protocol buffers 之前需要安裝許多的工具和庫。starter 項目中包含了一個名為 protoInstallation.sh 的腳本幫你搞定了這些。它會在安裝之前檢查是否已經安裝過這些庫。 這個腳本需要花一點時間來安裝,尤其是安裝 Google 的 protocol buffer 庫。打開你的終端,cd 命令進入到 Starter 目錄執行下面這個命令:

$ ./protoInstallation.sh 復制代碼

注意:執行的過程中你可能會被要求輸入管理員密碼。

腳本執行完成后,再運行一次以確保的到以下輸出結果:

如果你看到這些,那表示腳本已經執行完畢。如果腳本執行失敗了,那檢查下你是不是輸入了錯誤的管理員密碼。并重新運行腳本;它不會重新安裝那些已經成功的庫。 這個腳本做了這些事:

  • 安裝 Flask 以運行 Python 本地服務。
  • 從 Starter/protobuf-3.1.0 目錄下生成 protocol buffer 編譯器。
  • 安裝 protocol buffer 的 Python 模塊,這樣服務端可以使用 Protobuf 庫。
  • 將 Swift Protobuf 插件 protoc-gen-swift 移至 /usr/local/bin. 使 Protobuf 編譯器可以生成 Swift 的結構。
  • 注意:你可以用編輯器打開 protoInstallation.sh 文件來了解這個腳本是如何工作的。這需要一定的 bash 基礎。

    好了,現在你已經做好了使用 protocol buffers 的所有準備工作。

    定義一個 .proto 文件

    .proto 文件定義了 protocol buffer 描述你的數據結構的 message。把這個文件中的內容傳遞給 protocol buffer 編譯器后,編譯器會生成你的數據結構。

    注意:在這篇教程中,你將使用 proto3 來定義 message,這是 protocol buffer 語言的最新版本。可以訪問Google’s guidelines以獲取更多的 proto3 的信息。

    用你最習慣的編輯器打開 ProtoSchema/contact.proto ,這里已經定義好了演講者的 message:

    syntax = "proto3";message Contact { // 1enum ContactType { // 2SPEAKER = 0;ATTENDANT = 1;VOLUNTEER = 2;}string first_name = 1; //3string last_name = 2;string twitter_name = 3;string email = 4;string github_link = 5;ContactType type = 6;string imageName = 7; };message Speakers { // 4repeated Contact contacts = 1; }; 復制代碼

    我們來看一下這里面包含了哪些內容:

    The?Contact model describes a person’s contact information. This will be displayed on their badges in the app.

  • Contact 模型用于描述名片信息。在 app 中會被顯示在 badges 頁。
  • 每一個 contact 應該分類,這樣才能區別出是訪客還是演講者。
  • proto 文件中的每一條 message 和 enum 必須指派一個增量且唯一的數字標簽。這些數字用來用于區分信息二進制格式的域,這很重要。訪問reserved fields可以了解更多關于標簽和域的信息。
  • Speakers 模型包含了 contacts 的集合,* repeated* 關鍵字表示一個對象的數組。
  • ##生成 Swift 結構 把 contact.proto 傳遞給 protoc 程序,proto 文件中的 message 將會被轉化生成 Swift 的結構。這些結構會遵循 ProtobufMessage.protoc 并提供 Swift 中構造、方法來序列化和反序列化數據的途徑。

    注意:想了解更多關于 Swift 的 protobuf API, 訪問蘋果的?Protobuf API documentation.

    在終端中,進入** Starter/ProtoSchema **目錄,用編輯器打開 protoScript.sh,你會看到:

    #!/bin/bash echo 'Running ProtoBuf Compiler to convert .proto schema to Swift' protoc --swift_out=. contact.proto // 1 echo 'Running Protobuf Compiler to convert .proto schema to Python' protoc -I=. --python_out=. ./contact.proto // 2 復制代碼

    這個腳本對 contact.proto 文件執行了兩次 protoc 命令,分別創建了 Swift 和 Python 的源文件。 回到終端,執行下面的命令:

    $ ./protoScript.sh 復制代碼

    你會看到以下輸出結果:

    Running ProtoBuf Compiler to convert .proto schema to Swift protoc-gen-swift: Generating Swift for contact.proto Running Protobuf Compiler to convert .proto schema to Python 復制代碼

    你已經創建好了 Swift 和 Python 的源文件。 在 ** ProtoSchema** 目錄下,你會看到一個 Swift 和一個 Python 文件。同時分別還有一個對應的 .pb.swift 和 .pb.py. pb 前綴表示這是 protocol buffer 生成的類。

    contact.pb.swift 拖到 Xcode 的 project navigator 下的 Protocol Buffer Objects 組. 勾上“Copy items if needed”選項。同時將 contact_pb2.py 拷貝到 Starter/Server 目錄。 看一眼 ** contact.pb.swift** 和 contact_pb2.py中的內容,看看 proto message 是如何轉換成目標語言的。 現在你已經有了生成好的模型對象了,可以開始集成了! ##運行本地服務器 示例代碼中包含了一個 Python 服務。這個服務提供了兩個 GET 請求:一個用來獲取參會者的名牌信息,另一個用來列出演講者。 這個教程不會深入講解服務端的代碼。盡管如此,你需要了解到它用到了由 protocol buffer 編譯器生成的 contact_pb2.py 模型文件。如果你感興趣,可以看一看 RWServer.py 中的代碼,不看也無妨(手動滑稽)。 打開終端并 cd 至 Starter/Server 目錄,運行下面的命令:

    $ python RWServer.py復制代碼

    運行結果如下:

    測試 GET 請求

    通過在瀏覽器中發起 HTTP 請求,你可以看到 protocol buffer 的原數據。 在瀏覽器中打開 http://127.0.0.1:5000/currentUser 你會看到:

    再試試演講者的接口,http://127.0.0.1:5000/speakers

    注意:測試 RWCards app的過程中你可以退出、中止和重啟本地服務以便調試。

    現在你已經運行了本地服務器,它使用的是由 proto 文件生成的模型,是不是很cooool?

    發起服務請求

    現在你已經把本地服務器跑起來了,是時候在 app 中發起服務請求了。**RWService.swift **文件中將 RWService 類替換成下面的代碼:

    class RWService {static let shared = RWService() // 1let url = "http://127.0.0.1:5000"private init() { }func getCurrentUser(_ completion: @escaping (Contact?) -> ()) { // 2let path = "/currentUser"Alamofire.request("\(url)\(path)").responseData { response inif let data = response.result.value { // 3let contact = try? Contact(protobuf: data) // 4completion(contact)}completion(nil)}} } 復制代碼

    這個類將用來與你的 Python 服務器進行交互。你已經實現了獲取當前用戶的請求:

  • shared 是一個發起網絡請求的單例。
  • getCurrentUser(_:) 方法通過 /currentUser 路徑發起了獲取用戶信息的網絡請求,后臺會返回一個硬編碼的用戶信息。
  • if let 獲取了數據。
  • data 中包含了服務端返回的 protocol buffer 二進制數據。 Contact 的構造器以 data 作為入參,解碼數據。
  • 解碼數據只需要把 protocol buffer 的數據傳遞給對象的構造器即可,不需要其他的解析。 Swift 的 protocol buffer 庫幫你處理了所有的事情。 現在請求已經完成,可以展示數據了。

    集成參會者的名片

    打開 CardViewController.swift 文件并在 viewWillAppear(_:) 之后添加下面這些代碼:

    func fetchCurrentUser() { // 1RWService.shared.getCurrentUser { contact inif let contact = contact {self.configure(contact)}} }func configure(_ contact: Contact) { // 2self.attendeeNameLabel.attributedText = NSAttributedString.attributedString(for: contact.firstName, and: contact.lastName)self.twitterLabel.text = contact.twitterNameself.emailLabel.text = contact.emailself.githubLabel.text = contact.githubLinkself.profileImageView.image = UIImage(named: contact.imageName) } 復制代碼

    這些方法會幫你取得服務端傳過來的數據,并用來配置名片:

  • fetchCurrentUser() 請求服務器去獲取當前用戶的信息,并使用 * contact* 來配置 * CardViewController*。
  • configure(_:) 通過傳入的 contact 配置UI。
  • 用起來很簡單,但是還需要拿到一個 ContactType 枚舉用來區分參會者的類型。

    自定義 Protocol Buffer 對象

    你需要添加一個方法來把枚舉類型轉換成 string, 這樣名片頁面才能顯示 SPEAKER 而不是一個數字0. 但是這有個問題,如果不重新生成 .proto 文件來更新 message,怎樣才能往模型里添加新功能呢?

    Swift extensions 可以搞定這個,它可以讓你添加一些信息到類中而不需要改變類本身的代碼。 創建一個名為 contact+extension.swift 的文件,并添加到 Protocol Buffer Objects 目錄。添加以下代碼:

    extension Contact {func contactTypeToString() -> String {switch type {case .speaker:return "SPEAKER"case .attendant:return "ATTENDEE"case .volunteer:return "VOLUNTEER"default:return "UNKNOWN"}} } 復制代碼

    contactTypeToString() 方法將 ContactType 映射成了一個對應的顯示用的字符串。 打開 CardViewController.swift 并添加下面的代碼到 configure(_:)

    self.attendeeTypeLabel.text = contact.contactTypeToString()復制代碼

    將代表contact type的字符串傳遞給了 * attendeeTypeLabel*。 最后在 viewWillAppear(_:) 中,applyBusinessCardAppearance() 之后添加下面代碼:

    if isCurrentUser {fetchCurrentUser() } else {// TODO: handle speaker } 復制代碼
    • isCurrentUser* 已經被硬編碼成 true, 當被設置為演講者時這個值會被修改。*fetchCurrentUser() * 方法在默認情況下會被調用,獲取名片信息并將其填充到名片上。 運行程序來看看參會者的名片頁面:

    集成演講者列表

    My Badge 選項卡完成后,我們來看看 Speakers 選項卡。 打開 RWService.swift 并添加下面的代碼:

    func getSpeakers(_ completion: @escaping (Speakers?) -> ()) { // 1let path = "/speakers"Alamofire.request("\(url)\(path)").responseData { response inif let data = response.result.value { // 2let speakers = try? Speakers(protobuf: data) // 3completion(speakers)}}completion(nil) } 復制代碼

    看上去很熟悉是吧,它和 getCurrentUser(_:) 類似,不過他獲取的是 Speakers 對象,包含了一個 contact 的數組,用于表示回憶的演講者。 打開 SpeakersViewModel.swift 并將代碼替換為:

    class SpeakersViewModel {var speakers: Speakers!var selectedSpeaker: Contact?init(speakers: Speakers) {self.speakers = speakers}func numberOfRows() -> Int {return speakers.contacts.count}func numberOfSections() -> Int {return 1}func getSpeaker(for indexPath: IndexPath) -> Contact {return speakers.contacts[indexPath.item]}func selectSpeaker(for indexPath: IndexPath) {selectedSpeaker = getSpeaker(for: indexPath)} } 復制代碼

    SpeakersListViewController 顯示了一個參會者的列表,SpeakersViewModel中包含了這些數據:從 /speakers 接口中獲取的contact對象組成的數組。 SpeakersListViewController將在每一行中顯示一個speaker。 viewmodel創建好了之后,就該配置cell了。打開 SpeakerCell.swift,添加下面的代碼到 SpeakerCell:

    func configure(with contact: Contact) {profileImageView.image = UIImage(named: contact.imageName)nameLabel.attributedText = NSAttributedString.attributedString(for: contact.firstName, and: contact.lastName) } 復制代碼

    傳入了一個contact對象并且通過其屬性來配置cell的 image 和 label。這個cell會顯示演講者的照片,和他的名字。 接下來,打開 SpeakersListViewController.swift 并添加下面的代碼到 *viewWillAppear(_:)*中:

    RWService.shared.getSpeakers { [unowned self] speakers inif let speakers = speakers {self.speakersModel = SpeakersViewModel(speakers: speakers)self.tableView.reloadData()} } 復制代碼

    getSpeakers(_:)發起了一個請求去獲取演講者列表的數據,創建了一個 * SpeakersViewModel 的對象,并返回 speakers。 tableview 接下來會更新這些獲取到的數據。 你需要給 tableview 的每一行指定一個speaker用于顯示。替換tableView(_:cellForRowAt:)*的代碼:

    let cell = tableView.dequeueReusableCell(withIdentifier: "SpeakerCell", for: indexPath) as! SpeakerCell if let speaker = speakersModel?.getSpeaker(for: indexPath) {cell.configure(with: speaker) } return cell 復制代碼

    getSpeaker(for:) 根據當前列表的 indexPath返回 speaker數據,通過cell的*configure(with:)*配置cell。 當點擊列表中的一個cell時,你需要跳轉到 CardViewController 展示選擇的演講者信息,打開 CardViewController.swift 并在類中添加這些屬性:

    var speaker: Contact?復制代碼

    后面會用到這個屬性用來傳遞選擇的演講者。將*// TODO: handle speaker*替換為:

    if let speaker = speaker {configure(speaker) } 復制代碼

    這個判斷用來確定 speaker 是否已經填充過了,如果是,調用 configure(),在名片上更新演講者的信息。 回到 SpeakersListViewController.swift 傳遞選擇的 speaker。在 *tableView(_:didSelectRowAt:)*中, performSegue(withIdentifier:sender:) 上方添加:

    speakersModel?.selectSpeaker(for: indexPath)復制代碼

    將 speakersModel 中的對應 speaker 標記為選中。 接下來,在*prepare(for:sender:)*的 vc.isCurrentUser = false: 之后添加下面的代碼:

    vc.speaker = speakersModel?.selectedSpeaker復制代碼

    這里講 selectedSpeaker 傳遞給了 * CardViewController* 來顯示。 確保你的本地服務還在運行當中,build & run Xcode。你會看到 app 已經集成了用戶名片,同時顯示了演講者的信息。

    你已經成功地用Swift的客戶端和Python的服務端,構建好了一個應用程序。客戶端和服務端同時使用了由 proto 文件創建的模型。如果你需要修改模型,只需要簡單地運行編譯器并重新生成,就能立刻得到兩端的模型文件!

    總結

    你可以從?這里下載到完成的工程。 在這篇教程中,你已經學習到了 protocol buffer 的基本特征, 怎樣定義一個 .proto 文件并通過編譯器生成 Swift 文件。還學習了如何使用Flask 創建一個簡單的本地服務器,并使用這個服務發送 protocol buffer 的二進制數據給客戶端,以及如何輕松地去反序列化數據。 protocol buffers 還有更多的特性,比如說在 message 中定義映射和處理向后兼容。如果你對這些感興趣,可以查看 Google 的文檔。

    最后值得一提的是,Remote Procedure Calls這個項目使用了 protocol buffers 并且看起來非常不錯,訪問GRPC了解更多吧。

    總結

    以上是生活随笔為你收集整理的Protocol Buffers 在 iOS 中的使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。