iOS developer的良好習慣

前言

隱藏細節(jié),暴露抽象。

作為一名有追求的工程師,我們希望代碼能夠在版本迭代中逐漸優(yōu)化而不是劣化;同時也會學習掌握更多的技巧和工具,去更好的設計、實現(xiàn)和組織代碼。偶然看到一個apple工程師的分享,于是加上一些自己的經驗和感受,做一些總結。

正文

一、代碼組織

1、使用group

作為一名iOS工程師,Xcode應該是最熟悉的工具之一。舊版本的Xcode在新建一個目錄時,只會作為創(chuàng)建一個引用,不會同時在相同的路徑下去創(chuàng)建目錄。新版本Xcode創(chuàng)建目錄的時候都是以group的形式去創(chuàng)建,會在同級路徑下去創(chuàng)建對應的目錄。
比如下圖,在創(chuàng)建New Group的時候,就會同樣在Audio的目錄下去創(chuàng)建一個New Group的目錄。如果項目的代碼是很久以前的Xcode創(chuàng)建的,最好檢查一遍目錄,使得Xcode的工程文件目錄和實際的文件目錄結構保持一致;如果項目是新Xcode創(chuàng)建則盡量在Xcode中創(chuàng)建group。

2、拆分大文件

如果項目有使用storyboard,則可以把較大的storyboard文件,通過引用的方式拆分成多個storyboard。這樣能提升打開時的速度,也能使得多人協(xié)同開發(fā)時減少沖突的產生。
但是我經歷過的項目都沒有使用storyboard,大文件的矛盾更多是產生在.m文件,以一個我們項目中的文件為例:

這個2000行的.m文件并不是一蹴而就,而是隨著十幾個版本的迭代,邏輯不斷增加,慢慢變大的文件。這也是我們常說的歷史技術債務。技術債務產生的原因多種多樣,可能是最開始的時候沒有很好的框架設計,也可能是實現(xiàn)過程中有不規(guī)范的現(xiàn)象,又或者是多人協(xié)作開發(fā)導致的代碼膨脹。當發(fā)現(xiàn)問題之后,就需要去償還這個技術債務。

.m文件拆分首先需要把業(yè)務的核心邏輯梳理出來,抽象出來該模塊的狀態(tài)信息、關鍵參數(shù),將外部業(yè)務在.m內添加的邏輯改為依賴.m提供的狀態(tài),而狀態(tài)可以通過通知、消息等方式拋出去;
核心但是又內聚的邏輯可以使用xxLogic去封裝,然后.m文件直接依賴該xxLogic;也可以將其聚合到.m文件的一個Category之中;
經過這番處理,.m文件能得到極大瘦身,而梳理完內外部依賴之后,后續(xù)再新增邏輯也不用去查看.m文件,而是依賴下面的.h文件。

3、重視Xcode的提示

保持Xcode工程設置是最新的,使用Xcode自帶的性能優(yōu)化。當Xcode彈出下面這個框的提示時,如果沒有特殊訴求,apple工程師推薦點擊Perform Changes按鈕。

在編譯的過程中,Xcode給出的warning可能在線上運行時就是一個Bug。建議在debug開發(fā)階段,打開Treat Warnings as Errors選項;追本溯源,找到問題的根本原因,解決每一個編譯期間的warning。

如果是已知問題,暫無解決方案,為了避免阻塞編譯運行,可以使用xcode指令去忽略。(具體的warning類型可以在Xcode的Issue Navigator查看,快捷鍵是command+5)

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// ingore code
#pragma clang diagnostic pop
4、去掉無用的代碼

我們有時會提交一部分被注釋代碼,理由可能是代碼現(xiàn)在不需要但下個版本可能會用到,臨時注釋一下,反正不影響運行。但是設想一下,如果團隊里面每個人都有這個習慣,那么項目中是否會存在很多無用的代碼?并且這個代碼可能永遠也不會有用武之地。
所以,果斷地刪除那些無用代碼吧,即使真的有需要用到的時候,也可以通過代碼的版本控制工具去找到那些歷史代碼。

二、代碼管理

版本控制系統(tǒng)已經成為開發(fā)的必備工具之一。曾經svn也是版本管理的高效工具,Windows系統(tǒng)中的小烏龜(TortoiseSVN)非常好用。但是隨著git的出現(xiàn),svn已經被逐漸淘汰。

1、提交獨立

一個復雜功能往往由多個需求點組成,開發(fā)過程也可能持續(xù)數(shù)天時間。可以把需求的提交拆分成多次,盡量使得單次提交獨立,Xcode可以看到每一行代碼的提交備注信息。 換位思考,我們希望從git的commit信息里面,看到這段代碼的緣由。
點一下右邊對應信息,選擇show commit,還可以看到對應commit的具體內容。

一個人可以記住昨天為什么寫這段代碼,但很難記住一段數(shù)月乃至數(shù)年前的代碼為何出現(xiàn)。

2、分支管理

為了保持開發(fā)階段的便利,提供alpha分支,作為日常開發(fā)的合入分支;為了保證外網代碼的可查,提供beta分支,作為版本發(fā)布的打包分支;當版本發(fā)布之后,還需要打tag記錄對應版本,比如說release_1.0.0.10。
日常的需求開發(fā)(feature分支)、問題修復(bug分支)都是在非主干分支進行開發(fā),最終再合入alpha分支。合入的要求根據(jù)團隊實際情況,可以是分支驗收完成再合入,也可以合入后統(tǒng)一驗收。

3、Code Review

Code Review(代碼審查,后面簡稱CR)是發(fā)生在分支合入的情況,是成熟開發(fā)團隊必不可少的環(huán)節(jié)。CR有助于團隊代碼風格的統(tǒng)一,包括函數(shù)命名、變量命名、代碼組織風格等。同時,CR要求代碼具備一定的可讀性,也要求單次提交不過包括過多改動。

三、文檔

1、必要的注釋

好的代碼一目了然,能清晰描述邏輯,不需要注釋來輔助描述。但是一段特殊邏輯,需要有注釋來描述為何存在,以方便在改動之后去回歸影響點。
比如說一段經典的dispatch_after 1秒的邏輯,這1秒可能是為了避免某些異常case,也可能是產品側的需求要求。

2、對外方法的描述

平時的開發(fā)過程,除了注意變量和方法的命名要具有含義,對外提供方法的注釋可以清晰描述需要的參數(shù)。比如說下面的一個方法:


在Xcode中選擇對應的方法,按下快捷鍵option+?就可以看到該方法的描述,以及各個參數(shù)的要求。如果方法還沒添加描述,則按下option+command+?自動生成待補充的描述。

3、文檔積累

隨著業(yè)務的發(fā)展,項目中代碼不可避免的會快速膨脹,直接閱讀代碼會非常吃力。此時就需要有文檔來輔助了解各個模塊的情況。
文檔應當避免對具體邏輯細節(jié)的贅述,更是和從整體的設計和考慮的因素出發(fā),描述該模塊是如何運行起來。同時在設計的過程,也應該基于之前的技術方案設計。

培養(yǎng)團隊的寫文檔習慣,每個版本前期組織技術方案評審,由負責較為復雜需求的工程師準備一份技術方案的設計文檔,可以達到事半功倍的效果。

四、便捷工具

大家提到Xcode的分析工具,第一反應往往是Instrucment中的工具集。但是實際開發(fā)中還有一些便捷工具。

1、Network Link Conditioner

模擬弱網絡環(huán)境,以前是在手機的設置-開發(fā)者-Network Link Conditioner可以去設置,現(xiàn)在真機連接之后可以在Xcode中按下command+shift+1,選擇對應的設備就可以選擇具體的網絡環(huán)境。


2、 Address Sanitizer

Address Sanitizer是內存錯誤檢測工具,通過malloc/free增加標記實現(xiàn)。
比如說下面這一段代碼,buf指針創(chuàng)建了1024內存,再手動釋放,然后再去訪問buf指針的元素。這段代碼編譯時正常,在運行時不一定會崩潰 ,有可能就會演化成一個偶現(xiàn)bug,難以定位。
在使用Address Sanitizer工具的時候,運行到130行時就會報錯:Use of dealloccated memory。

打開方式是在scheme選項中,勾選Address Sanitizer。

3、Thread Sanitizer

Thread Sanitizer是線程錯誤檢測工具,可以檢測到一些多線程數(shù)據(jù)訪問的錯誤,比如說下面的代碼。
sTestNum是靜態(tài)全局變量,創(chuàng)建了多個線程去操作該變量,會觸發(fā)Data Race

打開方式是在scheme選項中,勾選Thread Sanitizer。

Thread Sanitizer關注的是數(shù)據(jù)的多線程訪問,通過記錄內存的訪問來實現(xiàn),并不能定位到多線程的crash問題,比如下面這個crash:

4、Main Thread Checker

Main Thread Checker是多線程操作UI檢查工具,UI操作只能在主線程執(zhí)行,如果在子線操作則會觸發(fā)警告。

打開方式是在scheme選項中,勾選Main Thread Checker。

5、Debug Gauges

在debug運行程序的時候,Debug Gauge能快捷地查看CPU、Memory、Disk、Network信息。

打開方式是Xcode按下command+7。

五、開發(fā)建議

1、最小依賴原則

一段邏輯的運行,往往需要外部的變量輸入。有時候為了便捷開發(fā),函數(shù)調用時候不會傳遞參數(shù),而是通過全局變量、self指針等直接去獲取需要的數(shù)據(jù)。但是這樣會導致代碼邏輯紊亂。在編碼的時候,非常建議使用最小依賴原則:盡可能少的使用外部依賴。
以函數(shù)為例,一個xx邏輯處理的方法應該只依賴函數(shù)參數(shù)。這樣函數(shù)的輸入輸出是固定的,即使函數(shù)放到其他地方,只要保證函數(shù)的輸入不變,則邏輯的輸出是不變的。
同理,除了函數(shù)還有view、model等等,盡可能少的去依賴外部數(shù)據(jù)、外部模塊,則該處邏輯更加獨立,更容易實現(xiàn)可以直接復用的view、model等等。

2、組件化&模塊化

實現(xiàn)功能的時候,應盡可能去除耦合;特定功能組成的庫就是組件,寫新功能代碼盡可能要往組件方向實現(xiàn);而模塊化指的是根據(jù)業(yè)務形態(tài),把代碼按照功能、業(yè)務進行聚合,相當于組合了各種組件和業(yè)務邏輯的庫。
模塊化和組件化等一個重要特點就是Pod化,將這些特定、獨立的功能代碼和業(yè)務代碼從主工程中剝離,抽象出來業(yè)務需要的接口,再重新通過pod依賴引入主工程。在這個過程,不單單是把代碼轉移到Pod庫,還需要做一些業(yè)務的解耦和依賴抽象。
好處也是顯而易見:
開發(fā)上,模塊化后各個業(yè)務相對獨立,能夠更加專注自己業(yè)務邏輯,即使業(yè)務出錯影響面也比較可控;
效率上,模塊化后可以做二進制組件,加快編譯速度;
管理上,組件owner的意識更強,方便添加數(shù)據(jù)監(jiān)控;
架構上,強迫面向接口編程,避免大量耦合的膠水代碼。

總結

本文部分參考自 WWDC2019,結合一些工作經驗,做了更適合自己的闡述。
自己也梳理了接下來一段時間的技術優(yōu)化方向:
日常業(yè)務迭代,通過CR保證新增代碼風格統(tǒng)一;
復雜業(yè)務需求,需要做技術方案評審,集思廣益;
已有歷史債務,小模塊微整實現(xiàn),大業(yè)務走專項重構,注意人力投入、業(yè)務影響和收益評估。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 本文內容基于 WWDC19 - 239 Great Developer Habits 整理。 首先來說一個詞,提到...
    奚山遇白閱讀 333評論 0 0
  • iOS面試題目100道 1.線程和進程的區(qū)別。 進程是系統(tǒng)進行資源分配和調度的一個獨立單位,線程是進程的一個實體,...
    有度YouDo閱讀 30,120評論 8 137
  • iOS | 面試知識整理 - OC基礎 (二) 1.C和 OC 如何混編 xcode可以識別一下幾種擴展名文件: ...
    d76d0c9d2b04閱讀 583評論 0 2
  • 喜歡就關注我唄! 1.設計模式是什么? 你知道哪些設計模式,并簡要敘述? 設計模式是一種編碼經驗,就是用比較成熟的...
    iOS白水閱讀 1,174評論 0 2
  • 內容概覽 組織 版本控制 注釋 & 文檔 測試 分析 評估 解耦 依賴管理 組織 組織有序的工作空間可以大幅度提升...
    FicowShen閱讀 437評論 0 1

友情鏈接更多精彩內容