《帶你領略 iOS 知識體系的全貌》,今天繼續應用開發篇(下)。
在這一篇,我們會講到 iOS 開發中的 JSON 解析、佈局框架、富文本、TDD/BDD 和編碼規範。
26 | JSON 解析#
背景:不同編程語言之間進行數據通信,通信數據規範該如何確定?所以需要一種通用,而且各個編程語言都支持的數據格式。
接下來就輪到 JSON 出場了。
JSON 的全稱是 JavaScript Object Notation,可見最初是被設計為 JavaScript 語言的一個子集,但最終因為和編程語言無關,所以 JSON 就成為了一種開放標準的常見數據格式。
目前很多編程語言都支持了 JSON 的生成和解析,所以 JSON 這樣的數據格式就滿足了背景裡提到的需求。
JSON 基於兩種結構:
-
鍵值對集合:具體實現有字典、Hash 表、對象、結構體等,示例:
{"key1": "val1","key2": "val2"}
。 -
有序值列表:具體實現有數組、向量、列表等,示例:
[1,3,5]
。
JSON 的其它特點:
-
支持嵌套:上述值可以是字符串、數字、對象、數組、布爾值、空值中的任一種;
-
不支持註釋;
-
水平制表符、換行符、回車符都會被當做空格。
大多數語言的邏輯可以轉換成語法樹結構,而語法樹能夠用 JSON 來描述。所以 JSON 的使用場景有很多:
-
描述業務數據,使得業務數據能夠動態更新;
-
描述業務邏輯,以實現業務邏輯的動態化;
-
描述頁面佈局。
如何解析?
蘋果原生提供了 NSJSONSerialization (OC) / JSONSerialization (Swift) 類來解析 JSON,相關的第三方框架有 JSONModel、Mantle、MJExtension、YYModel,它們都是基於原生類的封裝。
如果追求更高的解析性能,可以了解一下 simdjson(2019 年 2 月發布),號稱每秒可解析千兆字節 JSON 文件。相比按字節自頂向下遞歸解析的傳統 JSON 解析方式,其優化思路在於解析並行化,參考:
27 | 跟自動佈局比,Flexbox 好在哪?#
Flexbox 的 “地位”:是知名佈局庫 React Native、Weex 和 Texture(AsyncDisplayKit)採用的佈局思路,同時也是蘋果官方類 UIStackView 採用的佈局思路。
React Native 和 Weex 對 Flexbox 算法的實現,是一個叫作 Yoga 的 C++ 庫。
如下圖,Flexbox 算法的主要思想是,讓 flex container(容器)能夠改變其 flex item(項目的)寬高和順序,如通過擴大 / 縮小項目來適配(填充 / 防止超出)可用空間。
關於 Flexbox 更詳細的講解,可以參考:
-
Flex 佈局教程:語法篇—— 阮一峰
-
Flex 佈局教程:實例篇—— 阮一峰
-
Flex 佈局示例——Blog
那跟自動佈局相比,Flexbox 好在哪呢?
-
提供的佈局方法更方便、更全面、更規範;
-
響應式,跨平台性好:目前所有瀏覽器都已支持,同時在 iOS 和 Android 中也支持。
所謂響應式,是指不直接操作目標,而是通過代理達到操作的目的。
28 | 怎麼應對各種富文本表現需求?#
富文本是什麼?它是一段有屬性的字符串。
-
可以包含不同字體、不同字號、不同背景、不同顏色、不同字間距的文字;
-
還可以設置段落、圖文混排等等屬性。
然後,如何展示富文本呢?這裡分 2 種情況:
1)用 HTML 來描述的富文本
這種方式描述的富文本,可以直接使用 WKWebView 控件的 loadHTMLString:baseURL:
方法來展示。
此外,對於 HTML 裡的圖片資源,因為需要通過網絡請求來獲取,所以可以考慮緩存策略減少請求次數。
2)使用原生 iOS 代碼描述的富文本
長列表場景 (一次性返回多條數據交由前端渲染)一般對性能的要求更高,所以會使用這種方式描述的富文本。
這種方式描述的富文本,可使用蘋果官方的 TextKit 或者第三方的 YYText 來展示。
其中,YYText 有很多亮點:
-
在異步文字佈局和渲染上的性能非常好;
-
兼容 UILabel 和 UITextView;
-
自定義的 NSMutableAttributedString 分類,不光簡化了基類,還增加了嵌入 UIView、CALayer 等功能。
小結:
-
HTML 描述富文本更易讀、更容易維護;
-
原生代碼描述富文本的性能更高。
所以如果想結合兩者的優勢,可以使用 HTML 描述富文本,然後在展示前先將 HTML 轉成原生代碼(可參考作者的 HTN 項目,實現了 HTML 代碼轉原生代碼的能力)。
即富文本 → HTML → 原生代碼 → 展示。
29 | 如何進行 TDD 和 BDD?#
背景:編寫影響範圍比較大的代碼時,需要檢驗的地方就非常多,相應地,人工檢查的時間成本也會非常高。
那麼,如何提高編寫代碼後的檢驗效率呢?
答案是開發、測試同步進行,盡早發現問題。
從測試範圍上來劃分的話,測試可以分為:
-
單元測試(開發者負責)
-
集成測試(測試團隊負責)
-
系統測試(測試團隊負責)
其中,單元測試也叫模塊測試,這個單元可能是一個類的方法,也可能是一個模塊的某個函數;同時,開發者要注意保證每個單元的職責清晰。
從開發模式劃分的話,開發方式可以分為:
-
TDD(Test-driven development,測試驅動開發)
-
BDD(Behavior-driven development,行為驅動開發)
TDD 的開發思路是:
-
先編寫測試用例;
-
在不考慮代碼優化的情況下快速編寫功能實現代碼;
-
等功能開發完成後,在測試用例的保障下,進行代碼重構,以提高代碼質量。
它的測試用例主要針對開發中的最小單元,適合單元測試。
在思想上,TDD 和拿到功能需求後直接開發功能的區別是:
-
先考慮如何對功能進行測試,再考慮如何編寫代碼,這給優化代碼提供了更多的時間和空間;
-
即使幾個版本過後再來優化,只要能夠通過先前寫好的測試用例,就能夠保證代碼質量。
PS:有點 “不忘初心” 的那味道~
BDD 是 TDD 的進化,它:
-
基於行為進行功能測試,使用 DSL(Domain Specific Language,領域特定語言)來描述測試用例;
-
測試用例看起來和文檔一樣,更易讀、更好維護。
它的測試用例是對行為的描述,測試範圍更大一些,適合集成測試和系統測試。
同時,得益於 BDD 使用的 DSL 語言(規範、標準、可讀性高),不僅開發者可以使用 BDD 高效地發現問題,也方便測試團隊參與編寫。
BDD 的 OC 框架有 Kiwi、Specta、Expecta 等,Swift 框架有 Quick。
作者推薦 Kiwi 這個框架,因為它包含 Specta 的 DSL 模式,Expecta 框架的期望語法,以及 Mocks、Stubs 能力,具體使用可以參考 Kiwi Wiki(Specs | Expectations | Mocks and Stubs | Asynchronous Testing)。
Mocks:模擬對象,如模擬 Null 對象、模擬類的實例、模擬協議的實例。
Stubs:存根,可以讓選擇器或消息模式返回固定的結果,支持真實對象和模擬對象。
小結:
-
無論是 TDD 還是 BDD,都是先寫測試用例(需要考慮到各種異常條件以及輸入輸出的邊界),再開發代碼;
-
好的模塊化架構和 TDD 、BDD 是互相促進的。
作者建議:優先對基礎能力的功能開發使用 TDD 和 BDD,保證了基礎能力的穩定後,在時間允許的情況下,再考慮核心業務。
30 | 如何制定一套合適的編碼規範?#
背景:一個團隊有了統一的編碼規範,才能更有效地避免團隊成員相互認同感缺失的問題(代碼風格不一致而導致的)。
那什麼是好的代碼規範,需要考慮哪些方面呢?
首先,可以參看一些優秀公司的代碼規範:
-
Apple: Apple Developer Documentation | Technologies(接口設計)、Coding Guidelines for Cocoa
-
Google: Google Objective-C Style Guide 中文版、Google Swift Style Guide 中文版
-
Others: Spotify Objective-C Coding Style、NYTimes Objective-C Style Guide、Raywenderlich Objective-C Style Guide、Raywenderlich Swift Style Guide。
接下來,是一份簡單的參考:
-
常量:使用類型常量,而不是宏定義。
-
變量:明確體現出功能,最好加上類型做後綴;少用全局變量傳遞值,而是通過參數傳值(減少功能模塊間的耦合)。
-
屬性:OC 裡,儘量通過 get 方法來進行懶加載(避免無用的內存佔用和多餘的計算);Swift 裡,如果屬性是只讀的,可以省掉其 get 子句。
-
條件語句:減少或不使用默認處理,特別是使用 Switch 處理枚舉時(使用 Swift 編寫 Switch 語句時,如果不加 default 分支的話,當枚舉有新增值時,編譯器會提醒你增加分支處理);減少嵌套處理(增加可讀性),Swift 中可以充分利用 guard 語法。
-
循環語句:減少 continue 和 break 的使用(增加可讀性),Swift 中可以統一使用 guard 來代替。
-
函數:函數名體現目的;每個函數處理最小單位的邏輯,滿足單一職責原則;函數內儘量避免使用全局變量來傳遞數據(減少耦合,提高單元測試的準確性);注意檢查函數的入參(提高健壯性),Swift 裡的 guard 語法同樣適用於檢查入參。
-
類:在 OC 中,類的頭文件儘可能少地引入其他類的頭文件,而是通過
@class
來聲明,然後在實現文件裡再通過#import
引入需要的頭文件(使用@class
保證代碼編譯通過,使用#improt
保證代碼運行通過,參考 OC 中 @class 和 #import 的區別——CSDN);對於繼承和遵循協議的情況,無法避免引入其他類裡的頭文件,所以在代碼設計時儘量減少繼承(繼承關係太多不利於代碼的維護和修改,比如修改父類時還需要考慮對所有子類的影響)。 -
分類: 分類裡增加的方法名儘量加上前綴,如果是系統自帶類的分類的話,就一定要加上前綴(避免產生方法名重複的問題);把一個類裡的公有方法分類到不同的分類裡,便於管理維護(特別適合多人維護各自不同功能代碼的場景)。
💡:
-
代碼邏輯清晰是高質量代碼最基本、最必要的條件。如果代碼不清晰的話,那麼其他的擴展、重用、簡潔優雅都免談。
-
寫代碼的首要任務是能讓其他人看得懂,避免過度工程化(針對特定業務的工程設計)。
-
減少使用過新的語言特性和黑魔法,如果需要使用,則多補充註釋。
最後,如何將代碼規範落地執行呢?
最好的方式就是 Code Review(代碼審核)。通過 Code Review ,你可以:
-
檢查代碼規範是否被團隊成員執行;
-
同時及時指導代碼編寫不規範的同學。
Code Review 可以分為兩步走:
-
然後,再進行人工檢查(審核人可以點贊、評論),除了能達到將代碼規範落地的效果外,還可以促進成員之間的溝通交流和相互學習。
好啦,應用開發篇馬上就要告一段落(還差一篇介紹 iOS 開發學習資料的加餐),下次就要開始原理篇的內容了,涉及 iOS 系統內核 XNU、iOS 黑魔法背後的原理……(🤫留點想象空間)。
Bo2SS 會竭盡全力把它們講明白,還請大家多多支持~咱們下次見!