Swift代码规范

一. 宗旨

  • 尊重苹果原生的命名和书写规范
  • 能清晰表达含义
  • 简洁而不省略
  • 减少不必要的注释

二. 编码规范

1. 运算符

使用运算符(+,-,*, /,=, ==,->)的时候,前后加空格

    let a = 5 * 4 - 10

2. 逗号后面空格,冒号紧跟前面参数名,左大括号不用换行

    func add(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. 访问省略

类方法的点语法禁止省略,枚举的掉语法可以尽量省略。

    enum Gender {
        case male
        case female
    }
    
    let xiaomingSex : Gender = .male
    let xiaomingSkin : UIColor = UIColor.yellow

枚举可以通过上下文快速知道。类不可以。

5. 使用// MARK: -extension对代码按 功能/协议 分割

class <#Class#>: <#Class#> {
    // life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        
        baseSetting()
        
        initUI()
        
        sendNetworking()
    }
    
    // MARK: - Setter & Getter
}

//MARK: 通知回调,闭包回调,点击事件
extension <#Class#> {
    
}


//MARK: 网络请求
extension <#Class#> {
    
    func sendNetworking() { }
    
    func sendNetworking_one() { }
    
    func sendNetworking_two() { }
}

//MARK: UI的处理,通知的接收
extension <#Class#> {
    
    func baseSetting() {
        self.title = "<#...#>"
    }
    
    func initUI() {
        
    }
}

//MARK: 代理方法

6. 行缩进

缩进 4个空格代表一个缩进。 建议使用Xcode的缩进Control + i

三. 命名规范

1. 命名用语

命名用美式英语,不要使用中文或者拼音。

2. 尊重先例

尊重先例(苹果命名/行业默认命名规范。 命名数组用Array不用list。

3. 驼峰命名法

大驼峰 小驼峰 k前缀
说明 所有单词首字母都大写 第一个单词要小写,后面的单词首字母大写 k作为前缀的驼峰命名
使用 类、结构体、枚举、协议、文件名 变量、属性、函数、方法 非单例的静态常量命名

4. 文件命名

建议一个文件实现一个 Class。命名规则为 工程前缀 + 模块名 + 自定义 + 父类后缀.

    BBPhoneSeasonListViewController
    BBPhoneSeasonDetialViewModel
    BBLiveHomeCell
    BBLiveHomeItemView

5. 命名中出现缩写

  • 当命名里出现缩写词时,根据首字母全大写或小写。

  • TableViewCell 这种基本上不会和 CollectionViewCell 撞车,直接都叫 Cell。其它的大部分父类后缀维持就好.

6. 分类存放

文件按照功能分类存放

7. 命名Bool

is作为前缀。

8. 懒加载

声明对象的时候尽量使用懒加载。

    lazy var dataArrayM = NSMutableArray()

    lazy var bgView: UIView = {
        let view = UIView()
        // 属性赋值
        return view
    }()

9. 必要的时候使用介词,_默认值

使阅读更符合习惯,减少冗余词汇,提供更多的可能性。

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. 根据角色承担的任务命名变量、参数、关联,而不是类型限制

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. 创建型方法和创建型工厂方法的命名

  • 创建型方法: 使用initmake
    那么第一个参数的标签不要考虑组合成一个句子。因为这样的函数名称已经知道是要创建一个实例,那么参数再用介词修饰让句子流畅显得多余。正确的示范:
✅
let aColor = Color(red: 32, green: 64, blue: 128)
❌
let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)
  • 创建型工厂方法
    使用 make开头.
    SomeClass.makeButton()

四. 资源文件命名

1. 模块前缀

首先资源必须以 主要模块名称定义 中的名称开头。然后资源在模块名称下划线后,以驼峰命名法命名。例如:

    live_silverSeed.png
    live_goldSeed.png

资源较多分二级目录的情况下再加一层前缀,例如充电相关资源:

    misc/battery/misc_battery_6.png
    misc/battery/misc_battery_12.png

2. 功能后缀(可选)

第一层后缀以图片用途结尾,常见的几个定义如下:

类型 | 后缀
---- | ----
图标 | _ico
按钮 | _btn
背景 | _bg

加上后缀后的命名举例:

    recommend_refresh_btn.png

如果资源较少不加也能一眼看出来完全可以不加,资源多的话建议加上方便区分。

3. 状态后缀

第二层后缀描述图片所表达的状态:

状态 后缀
普通(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 是一系列关键帧组成的动画:

    common_loading_1.png
    ...
    common_loading_5.png

5. 分辨率后缀

最后就是大家都知道的分辨率后缀了。1x 资源是不需要加 @1x 的!2x,3x 资源请记得加上对应的后缀 @2x,@3x。

如果设计给了一张大图说所有设备统一用这个资源,记得把它当做 1x 资源。

五. 语法规范

1. 可选类型的拆包取值 if let

     let string : String? = "name"
        
     if let str == string {
          print(str)
     }

2. as! 尽量不要使用

多用 as???可选给默认值的形式

3. 常量的定义

如无必要尽量放到类里面。避免污染命名空间。

class Animal: NSObject {
    static let height: CGFloat = 100
}

4. 最短路径原则

根据判断条件的长短,选择使用if还是guard

5. 遍历数组

多用 forinmap flatMap等高级函数。

        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. 反向传值

反向传值统一使用Delegate。

  • 使用delegate的可以定义一次,声明多个方法( required or optional)。
  • 可以寻根溯源(能点进去)

六. 注释规范

1. 代码尽量不要注释,除非有必要注释。

需要注释的情况

  • 多含义的字段(status,type等)
  • 复杂的业务逻辑
  • 公共方法,公共类

2. 注释快捷键

  • 单行注释
    “command + /”,即“⌘ + /”
  • 多行注释
    /** ... */
  • 注释文档
    “command + Option + /”,即“⌘ + ⌥ + /”
    注释添加以后,在调用该函数时,按下“Option + 左键”,即“⌥ + 左键”,就能看到该函数的注释信息。
  • 注意区分 注释带标记属性的注释
    ///**/ 是注释 ////** */带有标记性质
  • 分组注释
    //MARK: -你要写的注释
  • todo注释
    //TODO:需要接着 写得东西
  • 警告注释
    // FIXME:要解决的BUG

七. 设计规范

遵循行业规范- 六大设计模式(solid规范)

  • 单一职责原则(Single Responsibility Principle, SRP)
  • 开闭原则(Open-Closed Principle, OCP)
  • 里氏代换原则(Liskov Substitution Principle, LSP)
  • 依赖倒转原则(Dependency Inversion Principle, DIP)
  • 接口隔离原则(Interface Segregation Principle, ISP)
  • 迪米特法则(Law of Demeter, LoD)

六大原则首字母去除重复的即 solid(固体的;可靠的;立体的;结实的;一致的)

1. 单一职责原则

一个类只负责一个功能领域中的相应职责,或者可以定义为:就一个类而言,应该只有一个引起它变化的原因。附详解

2. 开闭原则原则

一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。附详解

3. 里氏代换原则

所有引用基类(父类)的地方必须能透明地使用其子类的对象。附详解

4. 依赖倒置原则

抽象不应该依赖于细节,细节应当依赖于抽象。换言之,要针对接口编程,而不是针对实现编程附详解

5. 接口隔离原则

使用多个专门的接口,而不使用单一的总接口,即客户端不应该依赖那些它不需要的接口。附详解

6. 迪米特法则

一个软件实体应当尽可能少地与其他实体发生相互作用附详解

八. 组件化

从第一代码农写下第一行代码开始到上个世纪的80年代的软件危机,码农一直在考虑一个问题,怎么让写代码容易。

抛开找大牛,大神程序员这条路(你以为大牛,大神那么容易找啊),最后自然而然形成的一套思路就是大团队的协同合作(如同cpu发展史一样,从飙主频到飙核数)。

协同合作?—– 这个可就麻烦了。。。。团队。。。。还合作。。。。

几乎所有的码农开始代码的时候,写代码的都是以自我为中心的。怎么解释这种情况呢,就是cow code—牛仔代码,代码风格随意看心情。这就导致了写代码协作起来极为麻烦,为什么呢?我写代码的时候 ,我和上帝知道什么我写的什么,过了一个月就只有上帝知道写的什么了。

你这让人怎么干活。。。。活那么多。。。。人那么多。。。。相互坑不出活,老板会fire掉大家的。

很早就有人来想办法解决这个问题,在软代时代就已经有解决这个问题的法宝--组件化。当然那时候不是那么叫的,是通过两个原则来规范这个问题的,这两个原则就是:内聚性和耦合性。

意思就是:哥,我想按时回家哄妹子!!!你怎么写代码我不管,你的功能全在这你这儿实现(内聚性),不要让我还帮写你那块功能。另外,哥,求你了,你代码不要block(影响)我的代码(低耦合性)。

既然解决问题的思路在这儿,大牛们一代代前赴后继的在这条路上狂奔下去。

-----引用《知乎》解答


在一个项目越来越大,开发人员越来越多的情况下,项目会遇到很多问题。

  • 业务模块间划分不清晰,模块之间耦合度很大,非常难维护。
  • 所有模块代码都编写在一个项目中,测试某个模块或功能,需要编译运行整个项目。
  • 老大写的公共方法或组件老是被一些刁民妄想改动以便自己的功能。
  • 多个项目同时用一套架构,经常遇到版本不同步的困扰,新增了方法没法保证全部同步。
  • 同一个项目中协作开发,经常出现,"卧槽,谁TMD又动我的代码了"

在公司项目开发中,如果项目比较小,普通的单工程+MVC架构就可以满足大多数需求了。但是像淘宝、蘑菇街、微信这样的大型项目,原有的单工程架构就不足以满足架构需求了。

就拿淘宝来说,淘宝在13年开启的“All in 无线”战略中,就将阿里系大多数业务都加入到手机淘宝中,使客户端出现了业务的爆发。在这种情况下,单工程架构则已经远远不能满足现有业务需求了。所以在这种情况下,淘宝在13年开启了插件化架构的重构,后来在14年迎来了手机淘宝有史以来最大规模的重构,将其彻底重构为组件化架构。


如何实现组件化?
通过iOS开发伴侣cocoapods来管理组件化代码。大致意思就是将代码发布到cocoapods上面去。每个一个组件就相当于一个工程,通过cocopods来管理代码,在主工程中使用。一些方便公开的组件可以发布为公有库共所有人使用。不方便公开的组件,放到公司私有仓库里面仅供自己组员使用。


如何发布到cocoapods?

  1. 什么是cocoaPods?
  2. 如何编写podspec文件?
  3. 如何发布公有库?
  4. 如何发布私有库?

这样就达到了我们的目的将 图1 变成了图2 的样子.

Swift代码规范_第1张图片
图1
Swift代码规范_第2张图片
图2

也就达到了低耦合高内聚的目的了。


but 中间层是什么鬼?

中间层相当于电脑的cpu,来调度被组件化的模块。所有的模块都向中间层注册自己。各个模块之间的关系都是通过中间层联系的。

中间层即 路由!!!

九. 路由

思考如下的问题,平时我们开发中是如何优雅的解决的?

  • 3D-Touch功能或者点击推送消息,要求外部跳转到App内部一个很深层次的一个界面。
    比如微信的3D-Touch可以直接跳转到“我的二维码”。“我的二维码”界面在我的里面的第三级界面。或者再极端一点,产品需求给了更加变态的需求,要求跳转到App内部第十层的界面,怎么处理?
  • 自家的一系列App之间如何相互跳转?
    如果自己App有几个,相互之间还想相互跳转,怎么处理?
  • 如何解除App组件之间和App页面之间的耦合性?
    随着项目越来越复杂,各个组件,各个页面之间的跳转逻辑关联性越来越多,如何能优雅的解除各个组件和页面之间的耦合性?
  • 如何能统一iOS和Android两端的页面跳转逻辑?甚至如何能统一三端的请求资源的方式?
    项目里面某些模块会混合ReactNative,Weex,H5界面,这些界面还会调用Native的界面,以及Native的组件。那么,如何能统一Web端和Native端请求资源的方式?
  • 如果使用了动态下发配置文件来配置App的跳转逻辑,那么如果做到iOS和Android两边只要共用一套配置文件?
  • 如果App出现bug了,如何不用JSPatch,就能做到简单的热修复功能?
    比如App上线突然遇到了紧急bug,能否把页面动态降级成H5,ReactNative,Weex?或者是直接换成一个本地的错误界面?
  • 如何在每个组件间调用和页面跳转时都进行埋点统计?每个跳转的地方都手写代码埋点?利用Runtime AOP ?
  • 如何在每个组件间调用的过程中,加入调用的逻辑检查,令牌机制,配合灰度进行风控逻辑?
  • 如何在App任何界面都可以调用同一个界面或者同一个组件?只能在AppDelegate里面注册单例来实现?
    比如App出现问题了,用户可能在任何界面,如何随时随地的让用户强制登出?或者强制都跳转到同一个本地的error界面?或者跳转到相应的H5,ReactNative,Weex界面?如何让用户在任何界面,随时随地的弹出一个View ?

iOS界组价化方案

  • Protocol注册方案 (暂无了解)
  • URL注册方案
  • Target-Action runtime调用方案

MGJRoute方案

URL注册方案 蘑菇街 App 的组件化之路 已经说的很清楚了 可以去看下

原理:

  • 通过url注册服务, 其他地方通过url, 获取服务
  • 框架在维护一个url-block的表格

特点:

  • 每个业务组件, 都需要依赖这个框架
  • url维护成本高 硬解码
  • 可以在组件内部任何地方调用/注册服务, 没有必要统一组件接口服务

target-action方案

原理:

  • 每个组件, 提供一个统一披露的接口文件
  • 额外的维护一个中间件的分类扩展(在此处进行硬解码 通过运行时进行物理解耦)
  • 其他地方通过target-action;的方案进行交互

特点:

  • 集约
  • 统一了组件api服务
  • 组件与框架之间无依赖关系
  • 需要额外维护中间件类扩展

主讲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. 如何从路由中调用
        let vc = MCRoute.shared. CTRecommendViewController()

你可能感兴趣的:(Swift代码规范)