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()
^
:0: note: 'test' declared here
: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的二进制兼容做的尤其差,如果向外输出二进制库的话,增加、删除属性;新增、删除、调整接口顺序,都会导致二进制不兼容,需要更新主版本号。

你可能感兴趣的:(Swift规约)