一. 宗旨
- 尊重蘋果原生的命名和書寫規(guī)范
- 能清晰表達(dá)含義
- 簡潔而不省略
- 減少不必要的注釋
二. 編碼規(guī)范
1. 運(yùn)算符
使用運(yùn)算符(+轧拄,-坡脐,*, /司澎,=卢佣, ==重荠,->)的時候,前后加空格
let a = 5 * 4 - 10
2. 逗號后面空格,冒號緊跟前面參數(shù)名虚茶,左大括號不用換行
func add<T>(a: T, b: T) -> Int {
if let intA = a as? Int,let intB = b as? Int {
return intA + intB
}
fatalError()
}
3. 盡量不用self晚缩,除非有必要
var aName = "小明"
var bName = "小花"
func modifyName(aName: String, cName: String) {
self.aName = aName
bName = cName
}
4. 訪問省略
類方法的點(diǎn)語法禁止省略,枚舉的掉語法可以盡量省略媳危。
enum Gender {
case male
case female
}
let xiaomingSex : Gender = .male
let xiaomingSkin : UIColor = UIColor.yellow
枚舉可以通過上下文快速知道荞彼。類不可以。
5. 使用// MARK: -
和extension
對代碼按 功能/協(xié)議 分割
class <#Class#>: <#Class#> {
// life cycle
override func viewDidLoad() {
super.viewDidLoad()
baseSetting()
initUI()
sendNetworking()
}
// MARK: - Setter & Getter
}
//MARK: 通知回調(diào)待笑,閉包回調(diào)鸣皂,點(diǎn)擊事件
extension <#Class#> {
}
//MARK: 網(wǎng)絡(luò)請求
extension <#Class#> {
func sendNetworking() { }
func sendNetworking_one() { }
func sendNetworking_two() { }
}
//MARK: UI的處理,通知的接收
extension <#Class#> {
func baseSetting() {
self.title = "<#...#>"
}
func initUI() {
}
}
//MARK: 代理方法
6. 行縮進(jìn)
縮進(jìn) 4個空格代表一個縮進(jìn)。 建議使用Xcode的縮進(jìn)Control + i
三. 命名規(guī)范
1. 命名用語
命名用美式英語,不要使用中文或者拼音寞缝。
2. 尊重先例
尊重先例(蘋果命名/行業(yè)默認(rèn)命名規(guī)范癌压。 命名數(shù)組用Array不用list。
3. 駝峰命名法
大駝峰 | 小駝峰 | k前綴 | |
---|---|---|---|
說明 | 所有單詞首字母都大寫 | 第一個單詞要小寫荆陆,后面的單詞首字母大寫 | k作為前綴的駝峰命名 |
使用 | 類滩届、結(jié)構(gòu)體、枚舉被啼、協(xié)議帜消、文件名 | 變量、屬性浓体、函數(shù)泡挺、方法 | 非單例的靜態(tài)常量命名 |
4. 文件命名
建議一個文件實現(xiàn)一個 Class。命名規(guī)則為 工程前綴 + 模塊名 + 自定義 + 父類后綴
.
BBPhoneSeasonListViewController
BBPhoneSeasonDetialViewModel
BBLiveHomeCell
BBLiveHomeItemView
5. 命名中出現(xiàn)縮寫
當(dāng)命名里出現(xiàn)縮寫詞時命浴,根據(jù)首字母全大寫或小寫娄猫。
TableViewCell 這種基本上不會和 CollectionViewCell 撞車,直接都叫 Cell生闲。其它的大部分父類后綴維持就好.
6. 分類存放
文件按照功能分類存放
7. 命名Bool
以is
作為前綴媳溺。
8. 懶加載
聲明對象的時候盡量使用懶加載。
lazy var dataArrayM = NSMutableArray()
lazy var bgView: UIView = {
let view = UIView()
// 屬性賦值
return view
}()
9. 必要的時候使用介詞
,_
和默認(rèn)值
使閱讀更符合習(xí)慣碍讯,減少冗余詞匯褂删,提供更多的可能性。
extension UIBarButtonItem {
public static func setImage(_ image: UIImage?, on target: UIViewController, selector: Selector, isLeft: Bool = false) -> UIBarButtonItem {
return UIBarButtonItem.init()
}
}
10. 命名不歧義
extension SomeClass {
?
public mutating func remove(at position: Index) -> Element?
?
public mutating func remove(_ position: Index) -> Element?
?
public mutating func removeElement(_ member: Element) -> Element?
}
some.remove(at: x)
some.remove(x)
some.removeElement(x)
11. 根據(jù)角色承擔(dān)的任務(wù)命名變量冲茸、參數(shù)、關(guān)聯(lián)缅帘,而不是類型限制
let string = "Hello"
let greeting = "Hello"
12. 消除聲明角色的弱類型信息
?
func add(_ observer: NSObject, for keyPath: String)
?
func addObserver(_ observer: NSObject, forKeyPath path: String)
13. 閱讀更符合正常語法
// 正常語法
“x, insert y at z”
?
x.insert(y, at: z)
?
x.insert(y, position: z)
14. 創(chuàng)建型方法和創(chuàng)建型工廠方法的命名
- 創(chuàng)建型方法: 使用
init
或make
轴术。
那么第一個參數(shù)的標(biāo)簽不要考慮組合成一個句子。因為這樣的函數(shù)名稱已經(jīng)知道是要創(chuàng)建一個實例钦无,那么參數(shù)再用介詞修飾讓句子流暢顯得多余逗栽。正確的示范:
?
let aColor = Color(red: 32, green: 64, blue: 128)
?
let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
- 創(chuàng)建型工廠方法
使用make
開頭.
SomeClass.makeButton()
四. 資源文件命名
1. 模塊前綴
首先資源必須以 主要模塊名稱定義
中的名稱開頭。然后資源在模塊名稱下劃線后失暂,以駝峰命名法命名彼宠。例如:
live_silverSeed.png
live_goldSeed.png
資源較多分二級目錄的情況下再加一層前綴,例如充電相關(guān)資源:
misc/battery/misc_battery_6.png
misc/battery/misc_battery_12.png
2. 功能后綴(可選)
第一層后綴以圖片用途結(jié)尾弟塞,常見的幾個定義如下:
類型 | 后綴
---- | ----
圖標(biāo) | _ico
按鈕 | _btn
背景 | _bg
加上后綴后的命名舉例:
recommend_refresh_btn.png
如果資源較少不加也能一眼看出來完全可以不加凭峡,資源多的話建議加上方便區(qū)分。
3. 狀態(tài)后綴
第二層后綴描述圖片所表達(dá)的狀態(tài):
狀態(tài) | 后綴 |
---|---|
普通(Normal) | _n(可選决记,建議不加) |
高亮(Highlighted) | _h(必選) |
選中(Selected) | _s(必選) |
禁用(Disabled) | _d(必選) |
用上面的按鈕資源舉例:
recommend_refresh_btn_d.png
4. 特殊后綴
如果資源分成多塊之類的特殊情況摧冀,加一層特殊后綴,也用下劃線隔開。
common_tip.png 因拉伸需求需要拆分成左右兩塊:
common_tip_left.png
common_tip_right.png
common_loading 是一系列關(guān)鍵幀組成的動畫:
common_loading_1.png
...
common_loading_5.png
5. 分辨率后綴
最后就是大家都知道的分辨率后綴了索昂。1x 資源是不需要加 @1x 的建车!
2x,3x 資源請記得加上對應(yīng)的后綴 @2x椒惨,@3x缤至。
如果設(shè)計給了一張大圖說所有設(shè)備統(tǒng)一用這個資源,記得把它當(dāng)做 1x 資源康谆。
五. 語法規(guī)范
1. 可選類型的拆包取值 if let
let string : String? = "name"
if let str == string {
print(str)
}
2. as! 盡量不要使用
多用 as领斥?
和??
可選給默認(rèn)值的形式
3. 常量的定義
如無必要盡量放到類里面。避免污染命名空間秉宿。
class Animal: NSObject {
static let height: CGFloat = 100
}
4. 最短路徑原則
根據(jù)判斷條件的長短戒突,選擇使用if
還是guard
5. 遍歷數(shù)組
多用 forin
或map
flatMap
等高級函數(shù)。
let array = [1, 2, 3, 4, 5, 6]
for item in array {
print(item)
}
for (index, item) in array.enumerated() {
print(index,item)
}
array.map {
print($0)
}
6. 反向傳值
反向傳值統(tǒng)一使用Delegate描睦。
- 使用delegate的可以定義一次膊存,聲明多個方法(
required
oroptional
)。 - 可以尋根溯源(能點(diǎn)進(jìn)去)
六. 注釋規(guī)范
1. 代碼盡量不要注釋忱叭,除非有必要注釋隔崎。
需要注釋的情況
- 多含義的字段(status,type等)
- 復(fù)雜的業(yè)務(wù)邏輯
- 公共方法韵丑,公共類
2. 注釋快捷鍵
- 單行注釋
“command + /”爵卒,即“? + /”
- 多行注釋
/** ... */
- 注釋文檔
“command + Option + /”,即“? + ? + /”
注釋添加以后撵彻,在調(diào)用該函數(shù)時钓株,按下“Option + 左鍵”,即“? + 左鍵”,就能看到該函數(shù)的注釋信息陌僵。 - 注意區(qū)分
注釋
和帶標(biāo)記屬性的注釋
//
和/**/
是注釋///
和/** */
帶有標(biāo)記性質(zhì) - 分組注釋
//MARK: -你要寫的注釋
- todo注釋
//TODO:需要接著 寫得東西
- 警告注釋
// FIXME:要解決的BUG
七. 設(shè)計規(guī)范
遵循行業(yè)規(guī)范- 六大設(shè)計模式(solid規(guī)范)
- 單一職責(zé)原則(Single Responsibility Principle, SRP)
- 開閉原則(Open-Closed Principle, OCP)
- 里氏代換原則(Liskov Substitution Principle, LSP)
- 依賴倒轉(zhuǎn)原則(Dependency Inversion Principle, DIP)
- 接口隔離原則(Interface Segregation Principle, ISP)
- 迪米特法則(Law of Demeter, LoD)
六大原則首字母去除重復(fù)的即 solid(固體的轴合;可靠的;立體的碗短;結(jié)實的受葛;一致的)
1. 單一職責(zé)原則
一個類只負(fù)責(zé)一個功能領(lǐng)域中的相應(yīng)職責(zé),或者可以定義為:就一個類而言偎谁,應(yīng)該只有一個引起它變化的原因总滩。附詳解
2. 開閉原則原則
一個軟件實體應(yīng)當(dāng)對擴(kuò)展開放,對修改關(guān)閉巡雨。即軟件實體應(yīng)盡量在不修改原有代碼的情況下進(jìn)行擴(kuò)展闰渔。附詳解
3. 里氏代換原則
所有引用基類(父類)的地方必須能透明地使用其子類的對象。附詳解
4. 依賴倒置原則
抽象不應(yīng)該依賴于細(xì)節(jié)铐望,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象澜建。換言之向挖,要針對接口編程,而不是針對實現(xiàn)編程附詳解
5. 接口隔離原則
使用多個專門的接口炕舵,而不使用單一的總接口何之,即客戶端不應(yīng)該依賴那些它不需要的接口。附詳解
6. 迪米特法則
一個軟件實體應(yīng)當(dāng)盡可能少地與其他實體發(fā)生相互作用附詳解
八. 組件化
從第一代碼農(nóng)寫下第一行代碼開始到上個世紀(jì)的80年代的軟件危機(jī)咽筋,碼農(nóng)一直在考慮一個問題溶推,怎么讓寫代碼容易。
拋開找大牛奸攻,大神程序員這條路(你以為大牛蒜危,大神那么容易找啊),最后自然而然形成的一套思路就是大團(tuán)隊的協(xié)同合作(如同cpu發(fā)展史一樣睹耐,從飆主頻到飆核數(shù))辐赞。
協(xié)同合作?—– 這個可就麻煩了硝训。响委。。窖梁。團(tuán)隊赘风。。纵刘。邀窃。還合作。假哎。瞬捕。。
幾乎所有的碼農(nóng)開始代碼的時候舵抹,寫代碼的都是以自我為中心的肪虎。怎么解釋這種情況呢,就是cow code—牛仔代碼掏父,代碼風(fēng)格隨意看心情。這就導(dǎo)致了寫代碼協(xié)作起來極為麻煩秆剪,為什么呢赊淑?我寫代碼的時候 ,我和上帝知道什么我寫的什么仅讽,過了一個月就只有上帝知道寫的什么了陶缺。
你這讓人怎么干活。洁灵。饱岸。掺出。活那么多苫费。汤锨。。百框。人那么多闲礼。。铐维。柬泽。相互坑不出活,老板會fire掉大家的嫁蛇。
很早就有人來想辦法解決這個問題锨并,在軟代時代就已經(jīng)有解決這個問題的法寶--組件化。當(dāng)然那時候不是那么叫的睬棚,是通過兩個原則來規(guī)范這個問題的第煮,這兩個原則就是:內(nèi)聚性和耦合性。
意思就是:哥闸拿,我想按時回家哄妹子?张巍!新荤!你怎么寫代碼我不管揽趾,你的功能全在這你這兒實現(xiàn)(內(nèi)聚性),不要讓我還幫寫你那塊功能苛骨。另外篱瞎,哥,求你了痒芝,你代碼不要block(影響)我的代碼(低耦合性)俐筋。
既然解決問題的思路在這兒,大牛們一代代前赴后繼的在這條路上狂奔下去严衬。
-----引用《知乎》解答
在一個項目越來越大澄者,開發(fā)人員越來越多的情況下,項目會遇到很多問題请琳。
- 業(yè)務(wù)模塊間劃分不清晰粱挡,模塊之間耦合度很大,非常難維護(hù)俄精。
- 所有模塊代碼都編寫在一個項目中询筏,測試某個模塊或功能,需要編譯運(yùn)行整個項目竖慧。
- 老大寫的公共方法或組件老是被一些刁民妄想改動以便自己的功能嫌套。
- 多個項目同時用一套架構(gòu)逆屡,經(jīng)常遇到版本不同步的困擾,新增了方法沒法保證全部同步踱讨。
- 同一個項目中協(xié)作開發(fā)魏蔗,經(jīng)常出現(xiàn),"臥槽勇蝙,誰TMD又動我的代碼了"
在公司項目開發(fā)中沫勿,如果項目比較小,普通的單工程+MVC架構(gòu)就可以滿足大多數(shù)需求了味混。但是像淘寶产雹、蘑菇街、微信這樣的大型項目翁锡,原有的單工程架構(gòu)就不足以滿足架構(gòu)需求了蔓挖。
就拿淘寶來說,淘寶在13年開啟的“All in 無線”戰(zhàn)略中馆衔,就將阿里系大多數(shù)業(yè)務(wù)都加入到手機(jī)淘寶中瘟判,使客戶端出現(xiàn)了業(yè)務(wù)的爆發(fā)。在這種情況下角溃,單工程架構(gòu)則已經(jīng)遠(yuǎn)遠(yuǎn)不能滿足現(xiàn)有業(yè)務(wù)需求了拷获。所以在這種情況下,淘寶在13年開啟了插件化架構(gòu)的重構(gòu)减细,后來在14年迎來了手機(jī)淘寶有史以來最大規(guī)模的重構(gòu)匆瓜,將其徹底重構(gòu)為組件化架構(gòu)。
如何實現(xiàn)組件化未蝌?
通過iOS開發(fā)伴侶cocoapods來管理組件化代碼驮吱。大致意思就是將代碼發(fā)布到cocoapods上面去。每個一個組件就相當(dāng)于一個工程萧吠,通過cocopods來管理代碼左冬,在主工程中使用。一些方便公開的組件可以發(fā)布為公有庫共所有人使用纸型。不方便公開的組件拇砰,放到公司私有倉庫里面僅供自己組員使用。
如何發(fā)布到cocoapods狰腌?
這樣就達(dá)到了我們的目的將 圖1
變成了圖2
的樣子.
也就達(dá)到了低耦合高內(nèi)聚的目的了蹋笼。
but 中間層是什么鬼展姐?
中間層相當(dāng)于電腦的cpu躁垛,來調(diào)度被組件化的模塊。所有的模塊都向中間層注冊自己圾笨。各個模塊之間的關(guān)系都是通過中間層聯(lián)系的教馆。
中間層即 路由!@薮铩土铺!
九. 路由
思考如下的問題,平時我們開發(fā)中是如何優(yōu)雅的解決的板鬓?
- 3D-Touch功能或者點(diǎn)擊推送消息悲敷,要求外部跳轉(zhuǎn)到App內(nèi)部一個很深層次的一個界面。
比如微信的3D-Touch可以直接跳轉(zhuǎn)到“我的二維碼”俭令『蟮拢“我的二維碼”界面在我的里面的第三級界面〕唬或者再極端一點(diǎn)瓢湃,產(chǎn)品需求給了更加變態(tài)的需求,要求跳轉(zhuǎn)到App內(nèi)部第十層的界面赫蛇,怎么處理绵患? - 自家的一系列App之間如何相互跳轉(zhuǎn)?
如果自己App有幾個悟耘,相互之間還想相互跳轉(zhuǎn)落蝙,怎么處理? - 如何解除App組件之間和App頁面之間的耦合性?
隨著項目越來越復(fù)雜论笔,各個組件递瑰,各個頁面之間的跳轉(zhuǎn)邏輯關(guān)聯(lián)性越來越多,如何能優(yōu)雅的解除各個組件和頁面之間的耦合性奏寨? - 如何能統(tǒng)一iOS和Android兩端的頁面跳轉(zhuǎn)邏輯?甚至如何能統(tǒng)一三端的請求資源的方式鹰服?
項目里面某些模塊會混合ReactNative病瞳,Weex,H5界面悲酷,這些界面還會調(diào)用Native的界面套菜,以及Native的組件。那么设易,如何能統(tǒng)一Web端和Native端請求資源的方式逗柴? - 如果使用了動態(tài)下發(fā)配置文件來配置App的跳轉(zhuǎn)邏輯,那么如果做到iOS和Android兩邊只要共用一套配置文件顿肺?
- 如果App出現(xiàn)bug了戏溺,如何不用JSPatch渣蜗,就能做到簡單的熱修復(fù)功能?
比如App上線突然遇到了緊急bug旷祸,能否把頁面動態(tài)降級成H5耕拷,ReactNative,Weex托享?或者是直接換成一個本地的錯誤界面骚烧? - 如何在每個組件間調(diào)用和頁面跳轉(zhuǎn)時都進(jìn)行埋點(diǎn)統(tǒng)計?每個跳轉(zhuǎn)的地方都手寫代碼埋點(diǎn)闰围?利用Runtime AOP 赃绊?
- 如何在每個組件間調(diào)用的過程中,加入調(diào)用的邏輯檢查羡榴,令牌機(jī)制凭戴,配合灰度進(jìn)行風(fēng)控邏輯?
- 如何在App任何界面都可以調(diào)用同一個界面或者同一個組件炕矮?只能在AppDelegate里面注冊單例來實現(xiàn)么夫?
比如App出現(xiàn)問題了,用戶可能在任何界面肤视,如何隨時隨地的讓用戶強(qiáng)制登出档痪?或者強(qiáng)制都跳轉(zhuǎn)到同一個本地的error界面?或者跳轉(zhuǎn)到相應(yīng)的H5邢滑,ReactNative腐螟,Weex界面?如何讓用戶在任何界面困后,隨時隨地的彈出一個View 乐纸?
iOS界組價化方案
- Protocol注冊方案 (暫無了解)
- URL注冊方案
- Target-Action runtime調(diào)用方案
MGJRoute方案
URL注冊方案 蘑菇街 App 的組件化之路 已經(jīng)說的很清楚了 可以去看下
原理:
- 通過url注冊服務(wù), 其他地方通過url, 獲取服務(wù)
- 框架在維護(hù)一個url-block的表格
特點(diǎn):
- 每個業(yè)務(wù)組件, 都需要依賴這個框架
- url維護(hù)成本高 硬解碼
- 可以在組件內(nèi)部任何地方調(diào)用/注冊服務(wù), 沒有必要統(tǒng)一組件接口服務(wù)
target-action方案
原理:
- 每個組件, 提供一個統(tǒng)一披露的接口文件
- 額外的維護(hù)一個中間件的分類擴(kuò)展(在此處進(jìn)行硬解碼 通過運(yùn)行時進(jìn)行物理解耦)
- 其他地方通過target-action;的方案進(jìn)行交互
特點(diǎn):
- 集約
- 統(tǒng)一了組件api服務(wù)
- 組件與框架之間無依賴關(guān)系
- 需要額外維護(hù)中間件類擴(kuò)展
主講Target-Action
1. 向路由注冊控制器
/**
* 首頁推薦
*/
class Target_Recommend: NSObject,RouteTargetProtocol {
@objc func Action_ViewController(_ params: [String : Any]) -> UIViewController? {
let vc = CTRecommendViewController(params)
return vc
}
}
fileprivate let target_Recommend = "Recommend"
extension MCRoute {
/**
* 這個是首頁推薦
*/
public func CTRecommendViewController(_ dict : [String:Any]) -> UIViewController {
let vc = perform(target_Recommend, params: dict, shouldCacheTarget: false)
guard vc != nil else {
return errorController()
}
if let vc2 = vc as? CTRecommendViewController {
return vc2
}else {
return errorController()
}
}
}
2. 如何從路由中調(diào)用
let vc = MCRoute.shared. CTRecommendViewController()