Swift规约

语言规约

命名规范

  • 【强制】Swift并不需要使用;结束一行代码。

  • 【推荐】变量命名多参考苹果库或者优秀的开源库的命名方式。比如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命名倾向于不加任何前缀,但是仍然强制所有的自定义类型加上一个统一的前缀,比如阿里云App统一使用ALY。

  • 【强制】extension跟Objective-C一样,函数必须加一个前缀,比如aly_loadImage,便于理解和使用。不同的模块给同一个类增加相同命名的扩展,编译链接都不会有问题。但是如果同时import这些模块,调用同名的扩展方法会报下面这个错误。

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,使用大写,即使出现在方法和属性中。

代码组织

  • 【推荐】相同逻辑代码、同一个protocol函数的实现等,比如使用//MARK: ALYUIViewControllerRefreshDataProtocol标记,方便阅读代码。

  • 【推荐】类的属性使用lazy var实现,并且放到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关键字,否则会产生warning

@discardableResult
func request(_ urlRequest: URLRequestConvertible) -> DataRequest {
    return SessionManager.default.request(urlRequest)
}

避免严重的崩溃

  • 【推荐】不要强解Optional类型。强解非常危险,刚开始使用Swift开发非常容易在这块犯错误,导致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")

避免内存泄露

  • 【强制】闭包使用weak或者unowned避免循环依赖。如果明确外部变量在执行代码的过程中,不会变成nil,那么使用unowned,比如视觉元素的lazy代码块中。网络接口的回调是异步的,回调发生时,页面可能已经不存在了,这种场景下,需要使用weak
let cell = ALYCommonCellObject.Builder()
    .title(title: "使用许可协议", color: UIColor.aly_black)
    .selectionAction(select: { [unowned self] (cell) in
        self.utlogCounter("Setting", withMonitorPoint: "TermOfService")
    })
    .build()
  • 【强制】deinit里面要移除对所有通知和KVO的观察。

合理选择数据类型

  • 【推荐】尽量使用Swift的类型,而非Objective-C的桥接类型,比如使用URL而非NSURL,IndexPath而非NSIndexPath

  • 【参考】数据对象尽量使用struct,而非class。struct是Swift的基础类型,翻看苹果的基础库,可以发现所有的基础类型,比如Int、String等类型都是struct

  • 【强制】Swift支持字符串枚举类型,表达清晰,不易犯错,是一个非常好用的功能。

enum ALYVote : String {
    case approve = "approve"
    case clean   = "clean"
    case oppose  = "oppose"
}
  • 【强制】Swift 3.0新增了Decimal类型,使用的便捷性比之前桥接的NSDecimalNumber有质的飞越,需要使用高精度的场景,比如跟钱有关系的,一定要使用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

合理选择修饰符

  • 【推荐】函数和类的声明采用最小够用使用原则,加上private、final、open、public、internal(默认)等修饰符。
  • 函数使用final修饰会走静态分发,性能更好。
  • private修饰则不向外暴露,编译器优化可做内联。final和private都可以减少Xcode自动提示的信息量,提高Xcode的反应速度,好处多多。
  • 对于动态库暴露的类,open表示可以被继承的接口,public表示不能被继承的接口。明确不需要被外部继承使用的,请使用public关键字。
  • 【参考】关键的Swift代码,如果考虑未来需要打hotpatch,那么接口可以使用dynamic修饰,走Objective-C的动态派发。

使用尾随闭包

  • 【强制】如果函数接受一个闭包作为参数,那么将闭包放在最后一个位置,方便用户采用最简方式调用。

  • 【推荐】使用闭包的最简调用方式。

//最复杂版本
let fullGreetings = guestList.map({(person: String) -> String in return "Hello, \(person)!"})

//最简单版本
let fullGreetings = guestList.map{ "Hello, \($0)!" }

使用Swift的新方式

  • 【强制】统一使用下面这种单例的编写方式,非常简洁,混编的时候也能被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遍历数组非常简洁美观。map、filter能用的也尽量用起来吧。
self.groupList.forEach { (id, name) -> Void in
    vc.groupList[id] = name
}
  • 【推荐】defer可以简化异常处理逻辑,在作用域结束的时候,会执行defer代码块。
if exists(filename) {
    let file = open(filename, O_READ)

    defer close(file)

    while let line = try file.readline() {
        //
    }
}

优秀的Swift开发资源

  • 【推荐】尽量采用Swift开源库,减少混编的场景。

  • 【推荐】Swift处理JSON不是一件容易的事情,推荐使用HandyJSON。我们从Swift 2.x一直用到3.0,非常稳定且好用。

  • 【推荐】ObjectMapper是比较传统的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;
  • 【强制】构造函数务必返回instanceType。如果返回id,Swift必须要转型才能使用。
//返回id,在Swift就必须要转型了。
//+ (id)sharedInstantce;
//(TBLoginCenter.sharedInstantce() as? LoginProtocol)

//使用instanceType符合规范
+ (instanceType)sharedInstantce;
  • 【推荐】合理使用Nullability Annotations,让Swift更加理解Objective-C的语义。
- (__nullable id)itemWithName:(NSString * __nonnull)name;

//NS_ASSUME_NONNULL_BEGIN和NS_ASSUME_NONNULL_END将中间的代码都加上nonull,
//只需要对nullable的属性和参数单独声明就好了。
//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上会冲突,出现编译报错。如果不需要被继承,使用public。如果需要被继承,不采用WMO或不使用open关键字。
//可能会出现编译问题
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这样的类型,本身就不通过引用计数来管理内存,所以也不可能用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混着写的时候,有时候会出现OC的类在CloudConsoleApp-Bridging-Header.h里面提供给Swift使用,但是这个类又需要引入CloudConsoleApp-Swift.h使用Swift的一些功能,这样就循环包含了,没法玩下去了。

  • Swift的二进制兼容做的尤其差,如果向外输出二进制库的话,增加、删除属性;新增、删除、调整接口顺序,都会导致二进制不兼容,需要更新主版本号。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容