語言規(guī)約
命名規(guī)范
【強制】Swift并不需要使用;結束一行代碼。
【推薦】變量命名多參考蘋果庫或者優(yōu)秀的開源庫的命名方式柒巫。比如Swift 3.0開始,枚舉類型首字母都改成小寫,去掉了冗余信息腾它,比如
UIColor.redColor
變成UIColor.red力惯。argument label
也去掉了冗余信息碗誉,變得非常簡潔。
//Swift 2.3
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
//Swift 3.0父晶,cellForRowAtIndexPath簡化成cellForRowAt哮缺。
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
【強制】雖然Swift命名傾向于不加任何前綴,但是仍然強制所有的自定義類型加上一個統(tǒng)一的前綴甲喝,比如阿里云App統(tǒng)一使用ALY尝苇。
【強制】
extension
跟Objective-C一樣,函數(shù)必須加一個前綴埠胖,比如aly_loadImage
糠溜,便于理解和使用。不同的模塊給同一個類增加相同命名的擴展押袍,編譯鏈接都不會有問題诵冒。但是如果同時import
這些模塊,調(diào)用同名的擴展方法會報下面這個錯誤谊惭。
main.swift:10:5: error: 'test' is inaccessible due to 'internal' protection level
str.test()
^
<unknown>:0: note: 'test' declared here
<unknown>:0: note: 'test' declared here
- 【強制】專有名詞汽馋,如
ECS
,使用大寫圈盔,即使出現(xiàn)在方法和屬性中豹芯。
代碼組織
【推薦】相同邏輯代碼、同一個
protocol
函數(shù)的實現(xiàn)等驱敲,比如使用//MARK: ALYUIViewControllerRefreshDataProtocol
標記铁蹈,方便閱讀代碼。【推薦】類的屬性使用
lazy var
實現(xiàn)众眨,并且放到class
的后面握牧,方便閱讀代碼容诬。
lazy var textLabel : UILabel = {
let label = UILabel()
label.font = UIFont.aly_f10
label.textColor = UIColor.aly_ct_2
label.textAlignment = .center
label.text = "添加"
self.contentView.addSubview(label)
return label
}()
最佳實踐
消除警告
【強制】在
Build Settings
里面找到Swift Compiler - Warning Policies,
將Treat Warning as Erros
設置為Yes
沿腰,Swift這個設置跟Objective-C不在一起览徒,消除一切編譯警告非常有必要。【強制】返回值不需要強制使用的颂龙,請使用
@discardableResult
關鍵字习蓬,否則會產(chǎn)生warning
。
@discardableResult
func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
return SessionManager.default.request(urlRequest)
}
避免嚴重的崩潰
- 【推薦】不要強解
Optional
類型措嵌。強解非常危險躲叼,剛開始使用Swift開發(fā)非常容易在這塊犯錯誤,導致crash率居高不下企巢》憧叮可以通過guard let、if let包斑、??
來避免強解流礁。
//guard let適合后面有大量代碼依賴foo有值
guard let _foo = foo else {
return
}
print(_foo)
//if let比較靈活
if let _foo = foo {
//do something
}
//??更加靈活,但是一行代碼使用過多??可能有性能問題罗丰。
print(foo?? "hello world")
避免內(nèi)存泄露
- 【強制】閉包使用
wea
k或者unowned
避免循環(huán)依賴神帅。如果明確外部變量在執(zhí)行代碼的過程中,不會變成nil
萌抵,那么使用unowned
找御,比如視覺元素的lazy
代碼塊中搂擦。網(wǎng)絡接口的回調(diào)是異步的泌参,回調(diào)發(fā)生時,頁面可能已經(jīng)不存在了产园,這種場景下讨永,需要使用weak
滔驶。
let cell = ALYCommonCellObject.Builder()
.title(title: "使用許可協(xié)議", color: UIColor.aly_black)
.selectionAction(select: { [unowned self] (cell) in
self.utlogCounter("Setting", withMonitorPoint: "TermOfService")
})
.build()
- 【強制】
deinit
里面要移除對所有通知和KVO
的觀察。
合理選擇數(shù)據(jù)類型
【推薦】盡量使用Swift的類型卿闹,而非Objective-C的橋接類型揭糕,比如使用
URL而非NSURL,IndexPath而非NSIndexPath
锻霎。【參考】數(shù)據(jù)對象盡量使用
struct著角,而非class。struct
是Swift的基礎類型旋恼,翻看蘋果的基礎庫吏口,可以發(fā)現(xiàn)所有的基礎類型,比如Int、String
等類型都是struct
产徊。【強制】Swift支持字符串枚舉類型昂勒,表達清晰,不易犯錯舟铜,是一個非常好用的功能叁怪。
enum ALYVote : String {
case approve = "approve"
case clean = "clean"
case oppose = "oppose"
}
- 【強制】Swift 3.0新增了
Decimal
類型,使用的便捷性比之前橋接的NSDecimalNumber
有質(zhì)的飛越深滚,需要使用高精度的場景,比如跟錢有關系的涣觉,一定要使用Decimal
痴荐。
let foo : Decimal = 10.12373223423
let bar : Decimal = 1.23423432432432
print(foo+bar) //11.3579665585543176192
print(foo-bar) //11.3579665585543176192
print(foo*bar) //12.4950578137552000654026872358296354816
合理選擇修飾符
- 【推薦】函數(shù)和類的聲明采用最小夠用使用原則,加上
private官册、final生兆、open、public膝宁、internal
(默認)等修飾符鸦难。
- 函數(shù)使用
final
修飾會走靜態(tài)分發(fā),性能更好员淫。private
修飾則不向外暴露合蔽,編譯器優(yōu)化可做內(nèi)聯(lián)。final和private
都可以減少Xcode自動提示的信息量介返,提高Xcode的反應速度拴事,好處多多。- 對于動態(tài)庫暴露的類圣蝎,
open
表示可以被繼承的接口刃宵,public
表示不能被繼承的接口。明確不需要被外部繼承使用的徘公,請使用public
關鍵字牲证。
- 【參考】關鍵的Swift代碼,如果考慮未來需要打hotpatch关面,那么接口可以使用
dynamic
修飾坦袍,走Objective-C的動態(tài)派發(fā)。
使用尾隨閉包
【強制】如果函數(shù)接受一個閉包作為參數(shù)缭裆,那么將閉包放在最后一個位置键闺,方便用戶采用最簡方式調(diào)用。
【推薦】使用閉包的最簡調(diào)用方式澈驼。
//最復雜版本
let fullGreetings = guestList.map({(person: String) -> String in return "Hello, \(person)!"})
//最簡單版本
let fullGreetings = guestList.map{ "Hello, \($0)!" }
使用Swift的新方式
- 【強制】統(tǒng)一使用下面這種單例的編寫方式辛燥,非常簡潔,混編的時候也能被Objective-C識別。
class ALYXXX {
static let sharedInstance = ALYXXX()
private override init() {}
}
- 【推薦】多用
lazy var
聲明屬性挎塌,代碼緊湊徘六、好看、好維護榴都。
lazy var textLabel : UILabel = {
let label = UILabel()
label.font = UIFont.aly_f10
label.textColor = UIColor.aly_ct_2
label.textAlignment = .center
label.text = "添加"
self.contentView.addSubview(label)
return label
}()
- 【推薦】
foreach
遍歷數(shù)組非常簡潔美觀待锈。map、filter
能用的也盡量用起來吧嘴高。
self.groupList.forEach { (id, name) -> Void in
vc.groupList[id] = name
}
- 【推薦】
defer
可以簡化異常處理邏輯竿音,在作用域結束的時候,會執(zhí)行defer
代碼塊拴驮。
if exists(filename) {
let file = open(filename, O_READ)
defer close(file)
while let line = try file.readline() {
//
}
}
優(yōu)秀的Swift開發(fā)資源
【推薦】盡量采用Swift開源庫春瞬,減少混編的場景。
【推薦】Swift處理JSON不是一件容易的事情套啤,推薦使用HandyJSON宽气。我們從Swift 2.x一直用到3.0,非常穩(wěn)定且好用潜沦。
【推薦】ObjectMapper是比較傳統(tǒng)的JSON解析方式萄涯。如果場景比較簡單,也是不錯的選擇唆鸡。
【推薦】使用SnapKit寫AutoLayout約束涝影。
Swift與Objective-C混編
- 【強制】Swift不支持宏,所以要使用變量喇闸。
//#define NW_NETWOEK_STATUS_NOTIFY @"TBNetworkStatusChangeNotify"
extern NSString* const NW_NETWOEK_STATUS_NOTIFY;
- 【強制】Objective-C使用
typedef enum
定義的枚舉類型袄琳,Swift不能使用,需要使用NS_ENUM或者NS_OPTIONS
燃乍。
//typedef enum {
typedef NS_ENUM(NSUInteger, NetworkStatus) {
NotReachable = 0,
ReachableViaWiFi,
ReachableVia2G,
ReachableVia3G,
ReachableVia4G
};
//} NetworkStatus;
- 【強制】構造函數(shù)務必返回
instanceType
唆樊。如果返回id
,Swift必須要轉(zhuǎn)型才能使用刻蟹。
//返回id逗旁,在Swift就必須要轉(zhuǎn)型了。
//+ (id)sharedInstantce;
//(TBLoginCenter.sharedInstantce() as? LoginProtocol)
//使用instanceType符合規(guī)范
+ (instanceType)sharedInstantce;
- 【推薦】合理使用
Nullability Annotations
舆瘪,讓Swift更加理解Objective-C的語義片效。
- (__nullable id)itemWithName:(NSString * __nonnull)name;
//NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END將中間的代碼都加上nonull,
//只需要對nullable的屬性和參數(shù)單獨聲明就好了英古。
//iOS SDK慣用這種方法淀衣。可以多跳進去看看召调。
NS_ASSUME_NONNULL_BEGIN
@interface Foo : NSObject
@property (nonatomic, copy, nullable) NSString *bar1;
@property (nonatomic, copy) NSArray *bar2;
@end
NS_ASSUME_NONNULL_END
顯著的坑
-
open lazy var
和WMOSwift 3.0上會沖突膨桥,出現(xiàn)編譯報錯蛮浑。如果不需要被繼承,使用public
只嚣。如果需要被繼承沮稚,不采用WMO或不使用open關鍵字。
//可能會出現(xiàn)編譯問題
open lazy var promptTitleLabel : UILabel = {
let label = UILabel()
return label
}()
-
weak delegate
需要使用class
關鍵字册舞。否則會報如下的錯誤:'weak' cannot be applied to non-class type 'MyClassDelegate'蕴掏。
這是因為 Swift 的protocol
是可以被除了class
以外的其他類型遵守的,而對于像struct 或是 enum
這樣的類型调鲸,本身就不通過引用計數(shù)來管理內(nèi)存盛杰,所以也不可能用weak
這樣的ARC
的概念來進行修飾。
protocol MyClassDelegate: class {
func method()
}
class MyClass {
weak var delegate: MyClassDelegate?
}
class ViewController: UIViewController, MyClassDelegate {
// ...
var someInstance: MyClass!
override func viewDidLoad() {
super.viewDidLoad()
someInstance = MyClass()
someInstance.delegate = self
}
//...
}
用
private
修飾的類藐石,如果使用KVC
來給屬性設置值饶唤,編譯不會報錯,運行時也不會報錯贯钩,但就是設置不上。去掉private
就好了办素。Swift和OC混著寫的時候角雷,有時候會出現(xiàn)OC的類在
CloudConsoleApp-Bridging-Header.h
里面提供給Swift使用,但是這個類又需要引入CloudConsoleApp-Swift.h
使用Swift的一些功能性穿,這樣就循環(huán)包含了勺三,沒法玩下去了。Swift的二進制兼容做的尤其差需曾,如果向外輸出二進制庫的話吗坚,增加、刪除屬性呆万;新增商源、刪除、調(diào)整接口順序谋减,都會導致二進制不兼容牡彻,需要更新主版本號。