Swift编码规范

以下是一些常见的Swift编码规范,可以帮助保持代码一致性、可读性和可维护性。

1. 命名规范

  • 类名、结构体名、协议名:使用驼峰式命名(CamelCase),首字母大写。例如:MyClassUserProfile
  • 变量名、函数名、参数名:使用驼峰式命名(camelCase),首字母小写。例如:userNamecalculateArea()
  • 常量:使用驼峰式命名(camelCase),并且首字母小写。例如:maximumNumberOfFiles

2. 代码格式

  • 缩进:使用 4 个空格缩进,不要使用Tab。
  • 括号:左括号与关键词保持同一行,右括号单独占一行。例如:
    if condition {
        // code
    }
    
  • 换行:每行代码最多80-100个字符。超长的表达式应在运算符前换行。
  • 空格:在关键字和括号之间留一个空格,例如 if (condition),而在函数调用中不应在函数名和左括号之间留空格,例如 calculateArea(width: 5)

3. 注释

  • 使用//来写单行注释,使用/* ... */来写多行注释。
  • 注释应该简洁明了,解释为什么做某些操作,而不是解释“怎么做”。

4. 代码组织

  • 属性和方法的顺序:通常将常量属性放在最前面,变量属性其次,然后是初始化方法(如构造函数),接着是其他方法。
  • 分组相关代码:使用MARK:分隔符来分组相关功能的方法。例如:
    // MARK: - Lifecycle Methods
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    

5. 使用Self关键字

  • 只有在必要时使用self关键字,例如在闭包中捕获self或参数名与属性名冲突时。

6. 使用类型推断

  • 能使用类型推断时,尽量让编译器推断类型。避免冗长的类型声明。例如:
    let name = "John"  // 而不是 let name: String = "John"
    

7. 错误处理

  • 使用guard语句来提前退出错误情况,减少嵌套。
  • 使用do-catch来处理可能抛出错误的代码。

8. Optionals

  • 使用if letguard let来安全解包optional值。
  • 使用??操作符提供默认值,避免解包失败。

9. 集合和字面量

  • 使用简洁的字面量语法,例如[]表示空数组,[:]表示空字典。
  • 使用for-in循环来遍历数组、字典或其他集合。

10. 遵循协议的实现

  • 遵循协议的函数应该尽量放在一个extension中,使主类定义保持简洁。

11. 访问控制

  • 适当使用privatefileprivateinternalpublicopen来控制属性和方法的可见性,默认使用最低可见性。

12. 避免强制解包

  • 避免使用!来强制解包Optional变量,除非你明确知道它不会为nil。

13. 空数组和字典的初始化

  • 使用简洁的初始化语法:
    var names = [String]()
    var scores = [String: Int]()
    

14. 避免全局变量

  • 除非必要,避免使用全局变量。尽量在类或结构体内定义属性。

15. 命名文件

  • 文件名应与类、结构体或协议的名称一致。

16. 使用 Final 关键字

  • 在类、方法或属性前加上final关键字,防止它们被继承或重写,除非确实需要继承行为。这样可以提高性能并增加代码的安全性。

17. 尽量使用 Swift 标准库的功能

  • 使用Swift标准库提供的功能而不是自己实现。例如,使用mapfilterreduce等高阶函数来操作数组,而不是手动实现循环。

18. 避免过度使用嵌套

  • 复杂的嵌套结构会使代码难以理解。使用guard语句、早返回、分解函数和逻辑来减少嵌套层级。

19. 处理返回值

  • 如果函数有返回值,在需要的情况下捕获和使用它们。避免调用函数但忽略其返回值的情况。

20. 内存管理

  • 小心处理强引用循环,特别是在闭包中使用[weak self][unowned self]来避免强引用循环。
  • 理解ARC(自动引用计数)的工作原理,确保对象在不需要时被释放。

21. 使用Enums而不是Magic Numbers

  • 使用enum代替硬编码的常量或“魔法数字”。这样可以使代码更具可读性和可维护性。
enum UserType {
    case admin
    case regular
    case guest
}

22. 遵循 SOLID 原则

  • 尽量遵循面向对象设计的SOLID原则:单一职责原则、开闭原则、里氏替换原则、接口分离原则和依赖反转原则。这有助于使代码模块化、可扩展和易于维护。

23. 避免过长的函数

  • 函数应尽量短小,只执行单一职责。如果函数过长,考虑将其拆分为多个函数。

24. 使用合适的集合类型

  • 根据需求选择合适的集合类型(如ArraySetDictionary)。例如,如果不需要元素顺序且需要唯一性,使用Set

25. 使用字符串插值而非拼接

  • 使用字符串插值"\(variable)"而不是使用+运算符来拼接字符串,这样更简洁和高效。

26. 处理可选值时使用 Nil-Coalescing 操作符

  • 使用??提供默认值,以便在可选值为nil时使用默认值。
let value = optionalValue ?? "default value"

27. 类型别名

  • 当类型复杂且多次使用时,使用typealias为类型创建别名,增加代码可读性。
typealias CompletionHandler = (Result<Data, Error>) -> Void

28. Documentation(文档注释)

  • 使用///语法为公共API、类、方法和属性编写文档注释,以便生成文档并帮助其他开发人员理解代码。
/// 计算矩形的面积
/// - Parameters:
///   - width: 矩形的宽度
///   - height: 矩形的高度
/// - Returns: 矩形的面积
func calculateArea(width: Double, height: Double) -> Double {
    return width * height
}

29. 避免大块的代码注释

  • 如果某些代码不再使用,考虑删除而不是注释掉。版本控制系统如Git可以帮助跟踪代码的历史。

30. 定义初始化方法

  • 自定义的初始化方法应确保所有属性都被正确初始化。使用convenience initrequired init根据需要定义多种初始化方式。

31. 尽量避免使用全局函数

  • 尽量避免使用全局函数,除非这些函数与具体类型无关。使用静态方法或扩展来组织代码。

32. 类与结构体的选择

  • 使用结构体(struct)而非类(class),当数据是值类型且不会被继承时。结构体是默认不可变的,可以减少错误。

33. 保持一致性

  • 保持代码风格的一致性。团队成员之间应采用统一的编码风格,以便不同的人可以轻松理解和维护代码。

34. 自动化工具和Linting

  • 使用工具如SwiftLint来自动检查代码风格一致性和潜在错误。自动化工具可以帮助发现并遵循规范。

35. 性能优化

  • 使用lazy关键字延迟初始化,减少不必要的性能开销。
  • 只在必要时使用@escaping关键字来避免闭包导致的引用循环。

36. 保持接口简单

  • 尽量保持函数和方法的参数数量少于5个。过多的参数会增加函数的复杂性和使用难度。可以考虑使用结构体来传递多个参数。

37. 使用可变参数 (Variadic Parameters)

  • 当需要传递可变数量的相同类型参数时,使用可变参数。例如,定义一个函数可以接受多个字符串:
func logMessages(_ messages: String...) {
    for message in messages {
        print(message)
    }
}

38. 避免使用 Any 和 AnyObject

  • 尽量避免使用AnyAnyObject,除非绝对必要。明确的类型更容易理解和调试,使用泛型来保持类型安全。

39. 延迟属性初始化

  • 使用lazy关键字来延迟初始化那些可能在对象生命周期中不一定会用到的属性,从而提高性能:
lazy var expensiveObject: ExpensiveObject = {
    // 初始化代码
    return ExpensiveObject()
}()

40. 使用 defer 语句

  • 使用defer语句来确保在函数或方法退出前执行特定的清理操作,如关闭文件或释放资源。这在资源管理或处理错误时非常有用。
func readFile() {
    let file = openFile()
    defer {
        closeFile(file)
    }
    // 文件处理代码
}

41. 尽量避免强制类型转换 (Forced Casting)

  • 强制类型转换使用as!是不安全的,可能导致运行时崩溃。使用可选绑定(as?)或条件类型检查(if let)来处理转换。

42. 避免使用魔术字符串 (Magic Strings)

  • 不要在代码中直接使用字符串字面量作为标识符、键名或配置选项。使用常量或枚举来定义这些值,以减少拼写错误和维护难度。

43. 使用闭包简写语法

  • 当闭包参数名称没有歧义时,使用Swift的闭包简写语法来使代码更简洁:
let sortedNames = names.sorted { $0 > $1 }

44. 测试和单元测试

  • 编写单元测试来验证代码的正确性。使用XCTest框架来编写和运行测试。确保覆盖关键路径和边界情况。

45. 优化UI代码

  • 使用DispatchQueue.main.async确保UI更新在主线程上进行。
  • 在重绘和布局变化频繁的地方,如tableViewcollectionView的cell中,避免复杂的计算或数据处理。

46. 使用 Functional Programming Paradigms

  • 尽量使用Swift的函数式编程功能,如mapfilterreduce,而不是传统的循环。这可以使代码更简洁并减少错误。

47. 在对象创建时使用构造函数依赖注入

  • 通过构造函数传递依赖项而不是使用全局单例或依赖查找。这可以使对象更容易测试和复用。
class ViewController: UIViewController {
    private let dataService: DataService

    init(dataService: DataService) {
        self.dataService = dataService
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

48. 使用guard提高代码可读性

  • guard语句比if更适合用于条件提前退出,避免深度嵌套,并使代码更扁平化和易读。
func process(value: Int?) {
    guard let value = value else {
        return
    }
    // 使用 value 进行处理
}

49. 处理异步代码时使用 Task 和 await

  • Swift 5.5 引入了并发功能。使用async/await处理异步操作,这比传统的回调和闭包更易于理解和管理。
func fetchData() async throws -> Data {
    let data = try await URLSession.shared.data(from: url)
    return data
}

50. 使用扩展 (Extensions)

  • 使用扩展将相关的功能或特性分组,使代码更有组织性。将协议实现和特定功能分离到各自的扩展中。
extension User {
    func fullName() -> String {
        return "\(firstName) \(lastName)"
    }
}

51. 使用自定义操作符时保持谨慎

  • 自定义操作符可能会使代码更简洁,但过度使用或命名不清晰可能导致混淆。确保它们有明确的含义并与标准操作符一致。

52. 考虑可读性和维护性

  • 在代码优化和简化时,始终优先考虑可读性。写给其他开发者看的代码应该是清晰和易于理解的。

53. 正确处理 UI 状态

  • 避免直接操作 UI 组件的状态,特别是在多线程环境中。使用专门的状态管理工具或模式(如Redux、MVVM)来管理状态和视图更新。

54. 合理使用数据封装和隐私

  • 尽量将属性和方法设为privatefileprivate,只有在需要时才暴露为internalpublic。这有助于防止对象被意外修改。

55. 避免过度优化

  • 先让代码可读和工作正常,然后根据性能需求进行优化。过早的优化可能会增加复杂性并难以维护。

56. 使用访问控制明确表达意图

  • 访问控制不仅仅是为了安全性,它也表达了类、属性或方法的意图。使用privatefileprivateinternalpublicopen来明确哪些部分应该被访问或继承。

57. 使用typealias简化复杂类型

  • 当使用复杂类型时,例如闭包或元组,使用typealias可以提高可读性和易用性。
typealias CompletionHandler = (Result<Data, Error>) -> Void

58. 适当使用deinit进行清理

  • 在类中实现deinit方法,用于释放资源或进行必要的清理操作,避免内存泄漏或其他资源问题。

59. 避免无用的代码

  • 定期清理不再使用的代码、变量、函数和文件。保持代码库的简洁和整洁,有助于提高项目的可维护性。

60. 合理使用@available#available检查系统版本

  • 在处理不同iOS版本兼容性时,使用@available注解和#available语句来检查API的可用性。
if #available(iOS 15, *) {
    // 使用 iOS 15 的新特性
} else {
    // 使用兼容版本的实现
}

61. 使用@discardableResult修饰符

  • 如果函数返回值不一定需要处理,但你仍希望函数有返回值,可以使用@discardableResult来标记,以避免编译器警告。
@discardableResult
func performTask() -> Bool {
    // 执行任务
    return true
}

62. 避免使用嵌套的guardif语句

  • 当使用guardif语句时,尽量避免嵌套。过多的嵌套会使代码难以阅读和理解。使用早退出的方式可以简化逻辑。

63. 避免过度使用单例模式

  • 虽然单例模式有其用途,但过度使用会增加代码的耦合性和测试的难度。只在需要全局共享状态时使用单例。

64. 在适当时使用@frozen关键字

  • 对于不希望在未来扩展或修改的enumstruct使用@frozen关键字。这能提高优化和性能。
@frozen
enum Direction {
    case north
    case south
    case east
    case west
}

65. 遵循命令查询分离原则 (Command Query Separation, CQS)

  • 将命令(改变状态的方法)与查询(返回信息的方法)分离。一个方法要么是做事(有副作用),要么是返回值(无副作用),而不是两者都做。

66. 正确处理多线程

  • 使用GCD(Grand Central Dispatch)和OperationQueue来管理多线程操作。确保UI更新总是在主线程上进行,并避免数据竞争和死锁。

67. 使用mapflatMapcompactMapreduce等函数

  • 这些函数可以使集合操作更简洁和表达力更强。例如,将一个可选数组中的所有非nil值提取出来:
let numbers: [Int?] = [1, 2, nil, 4, nil]
let nonNilNumbers = numbers.compactMap { $0 }

68. 尽量避免在init方法中调用方法

  • 调用子类或自身的可被重写的方法可能导致未定义的行为,因为对象可能尚未完全初始化。

69. 使用协议而不是继承

  • 如果不需要共享具体实现细节,使用协议来定义接口而不是继承。这样可以减少耦合,提高灵活性和复用性。

70. 在扩展中实现协议

  • 将协议的实现放在扩展中,而不是在类定义中,这可以保持类定义的简洁和清晰。
class User: Codable {
    var name: String
}

extension User: CustomStringConvertible {
    var description: String {
        return "User(name: \(name))"
    }
}

71. 对大数组或集合的操作使用懒序列

  • 使用lazy属性来实现延迟求值,有助于提高性能,尤其是在处理大型集合时。
let largeArray = Array(1...1000).lazy.filter { $0 % 2 == 0 }.map { $0 * 2 }

72. 处理defer中的错误

  • defer语句块中处理可能的错误情况,这样可以确保在方法退出前执行必要的清理操作,即使出现了错误。
func processFile() {
    let file = openFile()
    defer {
        closeFile(file)
    }
    // 其他文件操作
}

73. 避免不必要的可变状态

  • 尽量使用let而不是var,减少状态的可变性。这有助于代码更加稳定和可靠。

74. 合理使用default分支

  • 在处理enum或其他明确类型时,尽量避免使用default分支,而是明确列出每种情况,以便在新增类型时可以发现编译错误。

75. 使用#warning#error标签标记待办事项

  • 使用#warning("To-Do: refactor this method")来标记需要进一步改进的代码,以提醒开发者后续处理。

76. 多使用Swift内建的调试工具

  • 善用Xcode的调试工具、仪器(Instruments)、性能分析工具(如Time Profiler)来监控和优化代码。

77. 使用@objc时的注意事项

  • 仅在需要与Objective-C互操作时使用@objc,这将影响性能并增加复杂性。

78. 避免魔法方法

  • 不要过度依赖于特殊命名的魔法方法,例如viewDidLoadviewWillAppear。虽然它们在特定框架中有特定意义,但过度使用或在自定义类中定义类似方法可能会导致混淆。

79. 优化内存使用

  • 使用弱引用(weak)或无主引用(unowned)来避免强引用循环,特别是在视图控制器和其视图模型之间。

80. 使用自定义日志功能

  • 使用自定义日志方法或框架来替代print,这样可以更好地控制日志级别(如Debug、Info、Warning、Error)并更方便地在生产环境中启用或禁用日志。

总结

这些附加的Swift编码规范和最佳实践帮助提升代码的安全性、可维护性和性能。遵循这些准则可以使代码更加清晰、易读、易于调试,并减少潜在的错误风险。在项目开发中,始终保持良好的编码习惯不仅能够提高代码质量,还能促进团队协作和项目的长期可持续性。

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

推荐阅读更多精彩内容

  • 温德里奇规范 本规范原文链接. 规范只是一些最佳实践, 请酌情使用. 1 正确性 第一条原则, 也是最根本的原则:...
    貘鸣阅读 1,558评论 1 0
  • Swift 编码规范 基本原则 参考资料 通用规则 格式 命名 编码风格 访问修饰符 Enum Optional ...
    wsj_2012阅读 1,947评论 0 4
  • 翻译作者:码农网 – 豆照建 没事多看看,规范一下swift 编码习惯。 1. 代码格式 1.1 使用四个空格进行...
    彡廿阅读 833评论 0 2
  • 官方 Swift 风格指南 一定要阅读Apple 的 API 设计规范。 具体的规范细节和附加说明如下。 本指南已...
    苹果上的小豌豆阅读 705评论 0 1
  • 注:以下皆为翻译,如有错误或疏漏,请指正。谢谢☺ 简介 (raywenderlich 版)您看到的这份规范可能与其...
    LovelyYilia阅读 4,351评论 1 17