## Objective-C代码
### 一、布局规范
#### 1、头文件引入
按照一定顺序引入头文件
```
#import <系统库>
#import <第三方>
#import "项目类"
```
#### 2、属性 和 私有变量
public 和 非public 属性建议放在相应的地方
```
@interface ClassA ()
@property (nonatomic,strong) NSString *name;
@end
```
私有变量建议命名遵循其中一个,为了规避iOS的命名,特性个人推荐第二种方法命名私有变量
```
NSString *_title;
NSString *title_;
```
#### 3、类内部布局
```
#pragma mark - lifeCycle (这部分写生命周期方法)
#pragma mark - <UITableViewDelegate> (系统的delegate方法)
#pragma mark - <CustomDelegate> (自定义的delegate方法)
#pragma mark - event response (交互方法)
#pragma mark - private methods (非公共方法)
#pragma mark - view getter(lazy) (主要是View懒加载)
#pragma mark - view setter
#pragma mark - data getter
#pragma mark - data setter
```
这个根据具体类的情况去自己可以增删
对于private methods 和 event response 等等只是在本类中调用的方法建议以p_开头,以后修改的时候能了解修改会造成的影响范围
例如:
```
#pragma mark -private methods
- (void)p_didSelectQuestionItem:(NSDictionary *)questionItem
#pragma mark -event response
- (void)p_searchEventClick:(UIButton *)button
```
### 二、注释
> 添加必要的注释
>
* 所有的property 需要给出注释;
>
* 所有自定义的方法需要给出注释;
>
* 比较大的代码块需要给出注释;
>
* 所有代码中出现的阿拉伯数字需要给出注释;
>
* 程序中出现加密/解密 逻辑的操作地方,需要给出注释说明过程(无论是系统还是自定义)。
#### 1、每个类头部有自己的独白 比如一个viewController是用来做什么的 可以首先写个注释说明下 其他的类雷同
```
/**
这是一个用来测试注释的viewController
注意点:
设计思路:
等等吧 你想说的告白都在这里
*/
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
}
- (void)viewDidLoad {
[super viewDidLoad];
}
@end
```
#### 2、单行,多行注释
单行注释 //+说明
```
// 初始化默认数据
[self p_setupDefaultData];
```
多行 /* */
```
/*!
XXX的状态
- XXXType_1: 孩子找妈妈中
- XXXType_1: 孩子找爸爸中
- XXXType_1: 孩子找小朋友中
*/
typedef NS_ENUM(NSUInteger,XXXType) {
XXXType_1,
XXXType_2,
XXXType_3,
};
```
方法注释
Commond+option+/
```
/**
向某人默认打招呼
@param name 某人
*/
- (void)sayHelloToPeople:(NSString *)name{
}
```
TODO标记
//TODO:XXX
用//TODO去做一些值得注意 或者 自己未完成的标志
```
- (void)sayHelloToPeople:(NSString *)name{
//TODO:向某人打招呼逻辑
}
```
### 三、一些缩进
#### 1、缩进
方法之间保留一行空行,
方法和属性的格式请按照以下格式书写:
```
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
```
```
- (void)viewDidLoad {
[super viewDidLoad];
}
```
```
@property (nonatomic, strong, readonly, nullable) NSIndexPath *previouslyFocusedIndexPath;
```
### 四、常见注意点:
- 尽量避免在程序中直接出现常数,使用超过一次的应以宏定义或枚举的形式来替代。
- 常数的宏定义应与它实际使用时的类型相一致。如以3.0来定义浮点类型,用3表示整型。常量的命名应当能够表达出它的用途,并且用大写字母表示。例如:#definePI 3.1415926
- 删除多余的空行、注释、方法以及未被使用的资源文件。
```
UIKIT_EXTERN NSNotificationName const UIKeyboardWillShowNotification;
```
```
UIKIT_EXTERN NSString *const UIKeyboardFrameBeginUserInfoKey
```
```
typedef NS_ENUM(NSInteger, NSWritingDirectionFormatType) {
NSWritingDirectionEmbedding = (0 << 1),
NSWritingDirectionOverride = (1 << 1)
}
```
```
typedef NSString * NSAttributedStringDocumentReadingOptionKey
```
---
---
## Swift 编程规范
> 良好的编程规范有利于项目的维护,同时也能避免因人为的疏忽产生的错误。本篇编码规范主要参考了 [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown)和[The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)当中的规范,并综合了实际开发中总结的经验。
### 正确性 (Correctness)
力争让你的代码没有编译警告(`warning`)。这个规则下延伸出了许多决定,如:使用`#selector`方式,而不是直接使用字符串。
### 命名 (Naming)
描述性和一致性的命名使得代码更易读、更易懂。一些规则如下:
* 追求调用方的清晰性
* 优先使用更清晰的命名,而不是更简洁的
* 使用驼峰样式
* 类型、协议名首字母大写,其他的都首字母小写
* 包含所有需要的单词,省略不必要的
* 使用基于角色的命名,而不是基于类型的
* 工厂方法使用`make`开头
* 对方法的命名
* 动词方法以`-ed`结尾,对于不可变的(`non-mutating`)动词方法用`-ing`结尾
* 布尔类型(`boolean`)应该以`is`开头
* 用于描述事物的协议(`protocols`)名称,用名词命名
* 用于描述能力的协议(`protocols`)名称,应以`-able` 或 `-ible`结尾
* 不要用生僻的单词
* 通常不要用缩写
* 选用好的参数名来起到描述的作用
#### 类前缀 (Class Prefixes)
Swift中的类型自动使用了其所在的模块(`module`)作为命名空间(`namespace`),所以你不必给类型加前缀。如果来自不同模块的类型名子冲突,可以显示的使用模块名作为调用前缀来避免冲突。
```swift
import SomeModule
import OtherModule
let someUsefulClass = SomeModule.UsefulClass()
let otherUsefulClass = OtherModule.UsefulClass()
```
#### 代理 (Delegates)
当定义一个代理方法时,第一个匿名参数应该是代理的源对象。(`UIKit`中有许多这样的的例子)
**推荐:**
```swift
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
```
**不推荐:**
```swift
/// `namePicker`应该是匿名的
func didSelectName(namePicker: NamePickerViewController, name: String)
/// 至少应包含`_ namePickerView: NamePickerView`参数,来作为代理的源对象
func namePickerShouldReload() -> Bool
```
#### 使用可类型推导的上下文 (Use Type Inferred Context)
利用好编译器的类型推导特性,来写出简短、清晰的代码。
**推荐:**
```swift
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
```
**不推荐:**
```swift
// 以下几种写法,都没有用到编译器的类型推导特性,写出来的代码较冗余
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
```
#### 泛型 (Generics)
泛型参数(`generic type parameters`)应当使用具有描述性的驼峰样式来命名。当泛型参数不具有明确的关系或角色时,可使用一个大写的字母表示即可。如:`T`,`U`,`V`
#### 使用的语言 (Language)
应使用美式英语的拼写方式以匹配Apple的API,应尽量保持命名的可读性,不应该用多含义且有相反或者混淆意思的单词。
**推荐:**
```swift
let color = "red"
```
**不推荐:**
```swift
let colour = "red"
```
### 代码组织 (Code Organization)
尽可能的使用扩展(`extensions`)来解耦你的代码,将代码划分到不同的扩展模块中,每个扩展应以`// MARK: -`开头,来更好的区分扩展。
#### 协议的遵守 (Protocol Conformance)
具体来讲,当让一个`Model`遵守某个协议时,推荐添加一个独立的`Model`扩展(`extensions`)来遵守该协议。这样使得相关的协议方法能组织在一起。
**推荐:**
```swift
class MyViewController: UIViewController {
// class stuff here
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// table view data source methods
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// scroll view delegate methods
}
```
**不推荐:**
```swift
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// all methods
}
```
对于`UIKit`的视图控制器(`view controllers`),可以考虑将生命周期(`lifecycle`)相关、自定义访问器(`custom accessors`)、`IBAction`独立不同的到类扩展中。
#### 无用的代码 (Unused Code)
无用的代码包括Xcode产生的模板代码、占位的注释等、方法的默认实现仅仅是调用`super`等,这些都应当移除掉。
**推荐:**
```swift
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Database.contacts.count
}
```
**不推荐:**
```swift
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return Database.contacts.count
}
```
#### 减小引入 (Minimal Imports)
保存最小的引入(`imports`)。例如, 当只使用`Foundation`时,不要引入`UIKit`。
### 空格 (Spacing)
* 代码折行使用4个空格(`spaces`)
* 方法的大括号和其他大括号(`if`/`else`/`switch`/`while`等),其左括号必须要和语句在同一行,并且右括号要换行;
* 变量类型和冒号(:)之间保留一个空格;
* 返回值标记`->`两侧各保留一个空格;
**推荐:**
```swift
if user.isHappy {
// Do something
} else {
// Do something else
}
```
**不推荐:**
```swift
if user.isHappy
{
// Do something
}
else {
// Do something else
}
```
* 各个方法之间必须有一个空行,这使得代码视觉上更清晰。
* 方法的实现中,应当适当的添加空行来划分功能。过多的空行意味着你应该拆分这些功能到不同的方法中,通过这样来避免一个巨大的方法。
* 通常冒号(Colons)的左边应当没有空格,而在右边有一个空格。
例外:
1. 三目运算符(`ternary operator`) `? :`
```swift
let foo = isEnable ? "A" : "B"
```
2. 空字典(`empty dictionary `) `[:]`
```swift
let bar: [String: Int] = [:]
```
3. `#selector`语法的无参方法`(_:)`
```swift
let sel = #selector((_:))
```
**推荐:**
```swift
/// 注意使用空格的位置
class TestDatabase: Database {
var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}
```
**不推荐:**
```swift
class TestDatabase : Database {
var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}
```
### 注释 (Comments)
* 给方法或属性添加注释,使用`option + command + /`来让Xcode自动生成
* 给关键逻辑添加一些局部注释
* 注释要保持最新状态
### 类和结构体 (Classes and Structures)
**用哪个呢?**
要知道结构体(`struct`)是值类型,当事物不具有唯一性时,使用结构体。比如,一个数组`[a, b, c]`,那么另一个数组`[a, b, c]`就跟它是一样的,他们之间可以互为替换,这时应当使用结构体来表示。
类(`class`)是引用类型,当事物具有唯一性或有明确的生命周期时,使用类。比如,一个电话簿列表,它的每一条记录都是唯一的,这时就要用类来表示。
以下是一个类的定义:
```swift
class Circle: Shape {
var x: Int
var y: Int
var radius: Double
var diameter: Double {
get {
return radius * 2
}
set {
radius = newValue / 2
}
}
init(x: Int, y: Int, radius: Double) {
self.x = x
self.y = y
self.radius = radius
}
convenience init(x: Int, y: Int, diameter: Double) {
self.init(x: x, y: y, radius: diameter / 2)
}
override func area() -> Double {
return Double.pi * radius * radius
}
}
extension Circle: CustomStringConvertible {
var description: String {
return "center = \(_centerString) area = \(area())"
}
private var _centerString: String {
return "(\(x),\(y))"
}
}
```
上面的代码遵循了以下规范:
* 属性、变量、常量、参数等语句的定义,都是在冒号后添加一个空格。如:`x: Int`、 `Circle: Shape`
* 方法之间保留一个空行
* 有默认的修饰符(如:`internal`)时不用重新添加。同样的,重载(`override`)方法时,不用重新添加访问修饰符`access modifier `
* 将功能组织整理到扩展中(`extensions`)
* 隐藏一些不必公开的实现细节,如`_centerString`属性用`private`来修饰。
#### 使用self (Use of Self)
在Swift中,可以省略`self`来访问一个对象的属性或方法。但为了消除使用的歧义,建议使用`self`来访问其属性或方法。
#### 计算属性 (Computed Properties)
为了简洁性,如果一个计算属性是只读(`read-only`)的,那么应省略`get { ... }`语句。`get { ... }`语句只在计算属性是读写(`read-write`)时,才要求使用。
**推荐:**
```swift
var diameter: Double {
return radius * 2
}
```
**不推荐:**
```swift
var diameter: Double {
get {
return radius * 2
}
}
```
#### 使用final标记 (Final)
出于某些原因,你可能不希望一个类被继承,这时你可以将其标记为`final`来表示它不能被继承。例如:一个单例,可能就不希望被继承。
### 函数声明 (Function Declarations)
将简短的函数声明保留在一行中,包括左括号。
```swift
func reticulateSplines(spline: [Double]) -> Bool {
/* code goes here */
}
```
无返回值的函数可以省略`-> Void`返回值
```swift
func foo(arg: Int) {
/* code goes here */
}
```
参数较多的函数可以像`Objective-C`中一样进行折行处理
```swift
func foo(arg1: Int,
arg2: Double,
arg3: String,
arg4: [Bool],
arg5: () -> Void) {
/* code goes here */
}
```
### 闭包表达式 (Closure Expressions)
当参数列表末尾只有一个闭包参数时,才应使用尾随闭包(`trailing closure`)语法。
**推荐:**
```swift
// 参数列表尾部只有一个闭包参数
UIView.animate(withDuration: 1.0) {
self.myView.alpha = 0
}
// 参数列表尾部有多个闭包参数
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}, completion: { finished in
self.myView.removeFromSuperview()
})
```
**不推荐:**
```swift
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
})
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}) { finished in
self.myView.removeFromSuperview()
}
```
对于单行的闭包,可以利用隐式返回(`implicit returns`)的特性:
```swift
list.sort { a, b in
a > b
}
```
对于使用闭包的链式调用,应当让代码更清晰、更易读。可以借助空格(`spacing`)、换行(`line breaks`)和匿名参数(`anonymous arguments`)等方法来让代码更清晰、更易读,但这都依赖于你的选择。
```swift
let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90)
let value = numbers
.map {$0 * 2}
.filter {$0 > 50}
.map {$0 + 10}
```
### 类型 (Types)
应尽量使用`Swift`的原生类型,当然`Swift`提供了对`Objective-C`类型的桥接方法,你可以使用`Objective-C的所有方法。
**推荐:**
```swift
let width = 120.0 // Double
let widthString = (width as NSNumber).stringValue // String
```
**不推荐:**
```swift
let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue // NSString
```
#### 常量 (Constants)
常量使用关键字`let`定义,变量使用关键字`var`定义。除非值可变,否则都应使用关键字`let`定义。
在一个类型的内部,通过关键字`static let`来定义静态常量,这样可以更好的组织这些静态常量。
**推荐:**
```swift
/// 将所有的接口都定义在一个类型内部,方便外部使用
struct API {
static let homeAPI: String = "https://www.example.com/home/"
static let mineAPI: String = "https://www.example.com/mine/"
}
enum Math {
static let e = 2.718281828459045235360287
static let pi = 3.14159265358979323846264
}
let l = 2 * Math.pi * r
```
**不推荐:**
```swift
let homeAPI: String = "https://www.example.com/home/"
let mineAPI: String = "https://www.example.com/mine/"
let e = 2.718281828459045235360287
let pi = 3.14159265358979323846264
let l = 2 * Math.pi * r
```
类内部的静态方法和属性,有点类似于全局方法和属性。但应尽量避免使用全局方法和属性。有一些例外情况,比如当使用到`runtime`的`objc_getAssociatedObject()`函数时,需要定义个全局的key来作为参数:
```swift
extension UIView {
var foo: Int? {
return objc_getAssociatedObject(self, &_fooKey) as? Int
}
}
private var _fooKey: Void?
```
#### 可选类型 (Optionals)
当使用`?`来定义可选类型时,表明它可以接受为`nil`的值。
当使用`!`来定义可选类型时,表明它可以接受为`nil`的值,但必须保证在使用它时,值不为`nil`。就像`ViewController`中的`view`在`viewDidLoad`被调用时就已经创建完成。
使用可选绑定(`optional binding`)来一次性解包单个或多个可选类型值。
**推荐:**
```swift
var foo: Int?
var bar: Bool?
if let foo = foo, let bar = bar {
// 同时解包foo和bar后才会执行这里
}
```
**不推荐:**
```swift
var foo: Int?
var bar: Bool?
if let foo = foo {
if let bar = bar {
// 同时解包foo和bar后才会执行这里
}
}
```
#### 懒加载 (Lazy Initialization)
使用懒加载(`lazy initialization`)来延迟初始化,这是一个很好的特性。如:
```swift
// 对象不需要进行配置
lazy var foo: SomeBigType = SomeBigType()
// 对象需要进行配置时,使用{ ... }()这种形式
lazy var bar: OtherBigType = { [unowned self] in
let obt = OtherBigType()
obt.delegate = self
return obt
}()
```
**注意:**
> 上面代码中使用到了`self`,所以需要使用`[unowned self]`来避免产生循环引用(`retain cycle`)
#### 类型推导 (Type Inference)
对于局部变量,尽量使用类型推导来让代码更紧凑。而对于成员变量来说,应尽量不要使用类型推导来让类的定义更清晰。
**推荐:**
```swift
struct Box: Shape {
var width: Int = 10
var height: Int = 10
var foo: [Int] = []
var bar: [String: Int] = [:]
func scale(rate: Int) {
let width = self.width * rate
let height = self.height * rate
self.width = width
self.height = height
}
}
```
**不推荐:**
```swift
struct Box: Shape {
var width = 10
var height = 10
var foo = [Int]()
var bar = [String: Int]()
func scale(rate: CGFloat) {
let width: Int = self.width * rate
let height: Int = self.height * rate
self.width = width
self.height = height
}
}
```
#### 语法糖 (Syntactic Sugar)
利用语法糖,使用更简短的声明方式:
**推荐:**
```swift
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
```
**不推荐:**
```swift
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>
```
### 内存管理 (Memory Management)
可以通过使用`weak` 和 `unowned`来避免循环引用,但也可以直接使用值类型(`struct`, `enum`)来避免循环引用。
通过`guard let`的形式来产生`strongSelf`:
**推荐:**
```swift
resource.request().onComplete { [weak self] response in
guard let strongSelf = self else {
return
}
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
```
**不推荐:**
```swift
// 当self的释放先于onComplete回调时,可能引起崩溃
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
```
**不推荐:**
```swift
// 当self的释放介于updateModel()和updateUI()方法之间时,可能会出现意想不到的情况
resource.request().onComplete { [weak self] response in
let model = self?.updateModel(response)
self?.updateUI(model)
}
```
### 访问控制 (Access Control)
一般来说,被标记为`private`、`fileprivate`的属性或方法都应以下划线(`_`)开头。
**推荐:**
```swift
private var _isEnabled: Bool
fileprivate var _isClosed: Bool
private func _foo() {
// code goes here
}
fileprivate func _bar() {
// code goes here
}
```
**不推荐:**
```swift
private var isEnabled: Bool
fileprivate var isClosed: Bool
private func foo() {
// code goes here
}
fileprivate func bar() {
// code goes here
}
```
但例外情况是,当标记被修饰为`private(set)`、`fileprivate(set)`时,不需要下划线(`_`)开头,因为他们都是可访问的属性,只是他们都是只读属性而已,如:
```swift
private(set) var isEnabled: Bool
fileprivate(set) var isClosed: Bool
```
一般将访问控制标记(`access control annotation`)放在声明的最前面,但例外情况是,当有属性标记`@IBAction`、`@IBOutlet`、`@discardableResult`、`@objc`时,需要将属性标记放在最前面。
```swift
private let _message = "Great Scott!"
class TimeMachine {
@IBOutlet private var _lbTitle: UILabel!
@objc private func _foo() -> Bool {
// code goes here
}
}
```
### 控制流 (Control Flow)
优先使用 `for-in` 的方式而不用`while`。
**推荐:**
```swift
for _ in 0..<3 {
print("Hello three times")
}
for (index, person) in attendeeList.enumerated() {
print("\(person) is at position #\(index)")
}
for index in stride(from: 0, to: items.count, by: 2) {
print(index)
}
for index in (0...3).reversed() {
print(index)
}
```
**不推荐:**
```swift
var i = 0
while i < 3 {
print("Hello three times")
i += 1
}
var i = 0
while i < attendeeList.count {
let person = attendeeList[i]
print("\(person) is at position #\(i)")
i += 1
}
```
### 使用Guard语句
通过使用`guard`来避免使用`if`时代码块嵌套过深的问题。
**推荐:**
```swift
func check(phone: String?, name: String?, age: Int) -> Bool {
guard let ph = phone, ph.count > 0 else {
return false
}
guard let nm = name, nm.count > 0 else {
return false
}
guard age > 0 else {
return false
}
return true
}
```
**推荐:**
```swift
func check(phone: String?, name: String?, age: Int) -> Bool {
// 把对phone、name、age的判断放到一个guard里,更简洁
guard
let ph = phone, ph.count > 0,
let nm = name, nm.count > 0,
age > 0 else {
return false
}
return true
}
```
**不推荐:**
```swift
func check(phone: String?, name: String?, age: Int) -> Bool {
if let ph = phone, ph.count > 0 {
if let nm = name, nm.count > 0 {
if age > 0 {
return true
} else {
return false
}
} else {
return false
}
} else {
return false
}
}
```
### 分号 (Semicolons)
在Swift中不要求必须加分号,一般情况下你不需要使用分号。只有当你希望将多行代码写在一行时,才需要加分号来断句。
### 圆括号 (Parentheses)
一般情况下,在类似`if`条件、`for`循环等控制语句中不需要加圆括号。只有当你在进行数学运算时,希望代码可读性更高时才需要适当的添加圆括号。
**推荐:**
```swift
let result = ((x * y) + 2) / h
```
**不推荐:**
```swift
let result = (x * y + 2) / h
```
### 引用
* [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown)
* [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)# 凡普信iOS代码规范V1.0
---
> 凡普信iOS开发端代码规范
---
## Objective-C代码规范
☺☺☺
<mark>
<br>把布局放在了第一位感觉这个是最重要的,其他的规范感觉大家平时都已经注意了,所以放在了后面 <br>
</mark>
☺☺☺
### 一、布局规范
#### 1、头文件引入
按照一定顺序引入头文件
```
#import <系统库>
#import <第三方>
#import "项目类"
```
#### 2、属性 和 私有变量
public 和 非public 属性建议放在相应的地方
```
@interface ClassA ()
@property (nonatomic,strong) NSString *name;
@end
```
私有变量建议命名遵循其中一个,为了规避iOS的命名,特性个人推荐第二种方法命名私有变量
```
NSString *_title;
NSString *title_;
```
#### 3、类内部布局
```
#pragma mark - lifeCycle (这部分写生命周期方法)
#pragma mark - <UITableViewDelegate> (系统的delegate方法)
#pragma mark - <CustomDelegate> (自定义的delegate方法)
#pragma mark - event response (交互方法)
#pragma mark - private methods (非公共方法)
#pragma mark - view getter(lazy) (主要是View懒加载)
#pragma mark - view setter
#pragma mark - data getter
#pragma mark - data setter
```
这个根据具体类的情况去自己可以增删
对于private methods 和 event response 等等只是在本类中调用的方法建议以p_开头,以后修改的时候能了解修改会造成的影响范围
例如:
```
#pragma mark -private methods
- (void)p_didSelectQuestionItem:(NSDictionary *)questionItem
#pragma mark -event response
- (void)p_searchEventClick:(UIButton *)button
```
### 二、注释
> 添加必要的注释
>
* 所有的property 需要给出注释;
>
* 所有自定义的方法需要给出注释;
>
* 比较大的代码块需要给出注释;
>
* 所有代码中出现的阿拉伯数字需要给出注释;
>
* 程序中出现加密/解密 逻辑的操作地方,需要给出注释说明过程(无论是系统还是自定义)。
#### 1、每个类头部有自己的独白 比如一个viewController是用来做什么的 可以首先写个注释说明下 其他的类雷同
```
/**
这是一个用来测试注释的viewController
注意点:
设计思路:
等等吧 你想说的告白都在这里
*/
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
{
}
- (void)viewDidLoad {
[super viewDidLoad];
}
@end
```
#### 2、单行,多行注释
单行注释 //+说明
```
// 初始化默认数据
[self p_setupDefaultData];
```
多行 /* */
```
/*!
XXX的状态
- XXXType_1: 孩子找妈妈中
- XXXType_1: 孩子找爸爸中
- XXXType_1: 孩子找小朋友中
*/
typedef NS_ENUM(NSUInteger,XXXType) {
XXXType_1,
XXXType_2,
XXXType_3,
};
```
方法注释
Commond+option+/
```
/**
向某人默认打招呼
@param name 某人
*/
- (void)sayHelloToPeople:(NSString *)name{
}
```
TODO标记
//TODO:XXX
用//TODO去做一些值得注意 或者 自己未完成的标志
```
- (void)sayHelloToPeople:(NSString *)name{
//TODO:向某人打招呼逻辑
}
```
### 三、一些缩进
#### 1、缩进
方法之间保留一行空行,
方法和属性的格式请按照以下格式书写:
```
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
```
```
- (void)viewDidLoad {
[super viewDidLoad];
}
```
```
@property (nonatomic, strong, readonly, nullable) NSIndexPath *previouslyFocusedIndexPath;
```
### 四、常见注意点:
- 尽量避免在程序中直接出现常数,使用超过一次的应以宏定义或枚举的形式来替代。
- 常数的宏定义应与它实际使用时的类型相一致。如以3.0来定义浮点类型,用3表示整型。常量的命名应当能够表达出它的用途,并且用大写字母表示。例如:#definePI 3.1415926
- 删除多余的空行、注释、方法以及未被使用的资源文件。
```
UIKIT_EXTERN NSNotificationName const UIKeyboardWillShowNotification;
```
```
UIKIT_EXTERN NSString *const UIKeyboardFrameBeginUserInfoKey
```
```
typedef NS_ENUM(NSInteger, NSWritingDirectionFormatType) {
NSWritingDirectionEmbedding = (0 << 1),
NSWritingDirectionOverride = (1 << 1)
}
```
```
typedef NSString * NSAttributedStringDocumentReadingOptionKey
```
---
---
## Swift 编程规范
> 良好的编程规范有利于项目的维护,同时也能避免因人为的疏忽产生的错误。本篇编码规范主要参考了 [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown)和[The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)当中的规范,并综合了实际开发中总结的经验。
### 正确性 (Correctness)
力争让你的代码没有编译警告(`warning`)。这个规则下延伸出了许多决定,如:使用`#selector`方式,而不是直接使用字符串。
### 命名 (Naming)
描述性和一致性的命名使得代码更易读、更易懂。一些规则如下:
* 追求调用方的清晰性
* 优先使用更清晰的命名,而不是更简洁的
* 使用驼峰样式
* 类型、协议名首字母大写,其他的都首字母小写
* 包含所有需要的单词,省略不必要的
* 使用基于角色的命名,而不是基于类型的
* 工厂方法使用`make`开头
* 对方法的命名
* 动词方法以`-ed`结尾,对于不可变的(`non-mutating`)动词方法用`-ing`结尾
* 布尔类型(`boolean`)应该以`is`开头
* 用于描述事物的协议(`protocols`)名称,用名词命名
* 用于描述能力的协议(`protocols`)名称,应以`-able` 或 `-ible`结尾
* 不要用生僻的单词
* 通常不要用缩写
* 选用好的参数名来起到描述的作用
#### 类前缀 (Class Prefixes)
Swift中的类型自动使用了其所在的模块(`module`)作为命名空间(`namespace`),所以你不必给类型加前缀。如果来自不同模块的类型名子冲突,可以显示的使用模块名作为调用前缀来避免冲突。
```swift
import SomeModule
import OtherModule
let someUsefulClass = SomeModule.UsefulClass()
let otherUsefulClass = OtherModule.UsefulClass()
```
#### 代理 (Delegates)
当定义一个代理方法时,第一个匿名参数应该是代理的源对象。(`UIKit`中有许多这样的的例子)
**推荐:**
```swift
func namePickerView(_ namePickerView: NamePickerView, didSelectName name: String)
func namePickerViewShouldReload(_ namePickerView: NamePickerView) -> Bool
```
**不推荐:**
```swift
/// `namePicker`应该是匿名的
func didSelectName(namePicker: NamePickerViewController, name: String)
/// 至少应包含`_ namePickerView: NamePickerView`参数,来作为代理的源对象
func namePickerShouldReload() -> Bool
```
#### 使用可类型推导的上下文 (Use Type Inferred Context)
利用好编译器的类型推导特性,来写出简短、清晰的代码。
**推荐:**
```swift
let selector = #selector(viewDidLoad)
view.backgroundColor = .red
let toView = context.view(forKey: .to)
let view = UIView(frame: .zero)
```
**不推荐:**
```swift
// 以下几种写法,都没有用到编译器的类型推导特性,写出来的代码较冗余
let selector = #selector(ViewController.viewDidLoad)
view.backgroundColor = UIColor.red
let toView = context.view(forKey: UITransitionContextViewKey.to)
let view = UIView(frame: CGRect.zero)
```
#### 泛型 (Generics)
泛型参数(`generic type parameters`)应当使用具有描述性的驼峰样式来命名。当泛型参数不具有明确的关系或角色时,可使用一个大写的字母表示即可。如:`T`,`U`,`V`
#### 使用的语言 (Language)
应使用美式英语的拼写方式以匹配Apple的API,应尽量保持命名的可读性,不应该用多含义且有相反或者混淆意思的单词。
**推荐:**
```swift
let color = "red"
```
**不推荐:**
```swift
let colour = "red"
```
### 代码组织 (Code Organization)
尽可能的使用扩展(`extensions`)来解耦你的代码,将代码划分到不同的扩展模块中,每个扩展应以`// MARK: -`开头,来更好的区分扩展。
#### 协议的遵守 (Protocol Conformance)
具体来讲,当让一个`Model`遵守某个协议时,推荐添加一个独立的`Model`扩展(`extensions`)来遵守该协议。这样使得相关的协议方法能组织在一起。
**推荐:**
```swift
class MyViewController: UIViewController {
// class stuff here
}
// MARK: - UITableViewDataSource
extension MyViewController: UITableViewDataSource {
// table view data source methods
}
// MARK: - UIScrollViewDelegate
extension MyViewController: UIScrollViewDelegate {
// scroll view delegate methods
}
```
**不推荐:**
```swift
class MyViewController: UIViewController, UITableViewDataSource, UIScrollViewDelegate {
// all methods
}
```
对于`UIKit`的视图控制器(`view controllers`),可以考虑将生命周期(`lifecycle`)相关、自定义访问器(`custom accessors`)、`IBAction`独立不同的到类扩展中。
#### 无用的代码 (Unused Code)
无用的代码包括Xcode产生的模板代码、占位的注释等、方法的默认实现仅仅是调用`super`等,这些都应当移除掉。
**推荐:**
```swift
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Database.contacts.count
}
```
**不推荐:**
```swift
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return Database.contacts.count
}
```
#### 减小引入 (Minimal Imports)
保存最小的引入(`imports`)。例如, 当只使用`Foundation`时,不要引入`UIKit`。
### 空格 (Spacing)
* 代码折行使用4个空格(`spaces`)
* 方法的大括号和其他大括号(`if`/`else`/`switch`/`while`等),其左括号必须要和语句在同一行,并且右括号要换行;
* 变量类型和冒号(:)之间保留一个空格;
* 返回值标记`->`两侧各保留一个空格;
**推荐:**
```swift
if user.isHappy {
// Do something
} else {
// Do something else
}
```
**不推荐:**
```swift
if user.isHappy
{
// Do something
}
else {
// Do something else
}
```
* 各个方法之间必须有一个空行,这使得代码视觉上更清晰。
* 方法的实现中,应当适当的添加空行来划分功能。过多的空行意味着你应该拆分这些功能到不同的方法中,通过这样来避免一个巨大的方法。
* 通常冒号(Colons)的左边应当没有空格,而在右边有一个空格。
例外:
1. 三目运算符(`ternary operator`) `? :`
```swift
let foo = isEnable ? "A" : "B"
```
2. 空字典(`empty dictionary `) `[:]`
```swift
let bar: [String: Int] = [:]
```
3. `#selector`语法的无参方法`(_:)`
```swift
let sel = #selector((_:))
```
**推荐:**
```swift
/// 注意使用空格的位置
class TestDatabase: Database {
var data: [String: CGFloat] = ["A": 1.2, "B": 3.2]
}
```
**不推荐:**
```swift
class TestDatabase : Database {
var data :[String:CGFloat] = ["A" : 1.2, "B":3.2]
}
```
### 注释 (Comments)
* 给方法或属性添加注释,使用`option + command + /`来让Xcode自动生成
* 给关键逻辑添加一些局部注释
* 注释要保持最新状态
### 类和结构体 (Classes and Structures)
**用哪个呢?**
要知道结构体(`struct`)是值类型,当事物不具有唯一性时,使用结构体。比如,一个数组`[a, b, c]`,那么另一个数组`[a, b, c]`就跟它是一样的,他们之间可以互为替换,这时应当使用结构体来表示。
类(`class`)是引用类型,当事物具有唯一性或有明确的生命周期时,使用类。比如,一个电话簿列表,它的每一条记录都是唯一的,这时就要用类来表示。
以下是一个类的定义:
```swift
class Circle: Shape {
var x: Int
var y: Int
var radius: Double
var diameter: Double {
get {
return radius * 2
}
set {
radius = newValue / 2
}
}
init(x: Int, y: Int, radius: Double) {
self.x = x
self.y = y
self.radius = radius
}
convenience init(x: Int, y: Int, diameter: Double) {
self.init(x: x, y: y, radius: diameter / 2)
}
override func area() -> Double {
return Double.pi * radius * radius
}
}
extension Circle: CustomStringConvertible {
var description: String {
return "center = \(_centerString) area = \(area())"
}
private var _centerString: String {
return "(\(x),\(y))"
}
}
```
上面的代码遵循了以下规范:
* 属性、变量、常量、参数等语句的定义,都是在冒号后添加一个空格。如:`x: Int`、 `Circle: Shape`
* 方法之间保留一个空行
* 有默认的修饰符(如:`internal`)时不用重新添加。同样的,重载(`override`)方法时,不用重新添加访问修饰符`access modifier `
* 将功能组织整理到扩展中(`extensions`)
* 隐藏一些不必公开的实现细节,如`_centerString`属性用`private`来修饰。
#### 使用self (Use of Self)
在Swift中,可以省略`self`来访问一个对象的属性或方法。但为了消除使用的歧义,建议使用`self`来访问其属性或方法。
#### 计算属性 (Computed Properties)
为了简洁性,如果一个计算属性是只读(`read-only`)的,那么应省略`get { ... }`语句。`get { ... }`语句只在计算属性是读写(`read-write`)时,才要求使用。
**推荐:**
```swift
var diameter: Double {
return radius * 2
}
```
**不推荐:**
```swift
var diameter: Double {
get {
return radius * 2
}
}
```
#### 使用final标记 (Final)
出于某些原因,你可能不希望一个类被继承,这时你可以将其标记为`final`来表示它不能被继承。例如:一个单例,可能就不希望被继承。
### 函数声明 (Function Declarations)
将简短的函数声明保留在一行中,包括左括号。
```swift
func reticulateSplines(spline: [Double]) -> Bool {
/* code goes here */
}
```
无返回值的函数可以省略`-> Void`返回值
```swift
func foo(arg: Int) {
/* code goes here */
}
```
参数较多的函数可以像`Objective-C`中一样进行折行处理
```swift
func foo(arg1: Int,
arg2: Double,
arg3: String,
arg4: [Bool],
arg5: () -> Void) {
/* code goes here */
}
```
### 闭包表达式 (Closure Expressions)
当参数列表末尾只有一个闭包参数时,才应使用尾随闭包(`trailing closure`)语法。
**推荐:**
```swift
// 参数列表尾部只有一个闭包参数
UIView.animate(withDuration: 1.0) {
self.myView.alpha = 0
}
// 参数列表尾部有多个闭包参数
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}, completion: { finished in
self.myView.removeFromSuperview()
})
```
**不推荐:**
```swift
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
})
UIView.animate(withDuration: 1.0, animations: {
self.myView.alpha = 0
}) { finished in
self.myView.removeFromSuperview()
}
```
对于单行的闭包,可以利用隐式返回(`implicit returns`)的特性:
```swift
list.sort { a, b in
a > b
}
```
对于使用闭包的链式调用,应当让代码更清晰、更易读。可以借助空格(`spacing`)、换行(`line breaks`)和匿名参数(`anonymous arguments`)等方法来让代码更清晰、更易读,但这都依赖于你的选择。
```swift
let value = numbers.map { $0 * 2 }.filter { $0 % 3 == 0 }.index(of: 90)
let value = numbers
.map {$0 * 2}
.filter {$0 > 50}
.map {$0 + 10}
```
### 类型 (Types)
应尽量使用`Swift`的原生类型,当然`Swift`提供了对`Objective-C`类型的桥接方法,你可以使用`Objective-C的所有方法。
**推荐:**
```swift
let width = 120.0 // Double
let widthString = (width as NSNumber).stringValue // String
```
**不推荐:**
```swift
let width: NSNumber = 120.0 // NSNumber
let widthString: NSString = width.stringValue // NSString
```
#### 常量 (Constants)
常量使用关键字`let`定义,变量使用关键字`var`定义。除非值可变,否则都应使用关键字`let`定义。
在一个类型的内部,通过关键字`static let`来定义静态常量,这样可以更好的组织这些静态常量。
**推荐:**
```swift
/// 将所有的接口都定义在一个类型内部,方便外部使用
struct API {
static let homeAPI: String = "https://www.example.com/home/"
static let mineAPI: String = "https://www.example.com/mine/"
}
enum Math {
static let e = 2.718281828459045235360287
static let pi = 3.14159265358979323846264
}
let l = 2 * Math.pi * r
```
**不推荐:**
```swift
let homeAPI: String = "https://www.example.com/home/"
let mineAPI: String = "https://www.example.com/mine/"
let e = 2.718281828459045235360287
let pi = 3.14159265358979323846264
let l = 2 * Math.pi * r
```
类内部的静态方法和属性,有点类似于全局方法和属性。但应尽量避免使用全局方法和属性。有一些例外情况,比如当使用到`runtime`的`objc_getAssociatedObject()`函数时,需要定义个全局的key来作为参数:
```swift
extension UIView {
var foo: Int? {
return objc_getAssociatedObject(self, &_fooKey) as? Int
}
}
private var _fooKey: Void?
```
#### 可选类型 (Optionals)
当使用`?`来定义可选类型时,表明它可以接受为`nil`的值。
当使用`!`来定义可选类型时,表明它可以接受为`nil`的值,但必须保证在使用它时,值不为`nil`。就像`ViewController`中的`view`在`viewDidLoad`被调用时就已经创建完成。
使用可选绑定(`optional binding`)来一次性解包单个或多个可选类型值。
**推荐:**
```swift
var foo: Int?
var bar: Bool?
if let foo = foo, let bar = bar {
// 同时解包foo和bar后才会执行这里
}
```
**不推荐:**
```swift
var foo: Int?
var bar: Bool?
if let foo = foo {
if let bar = bar {
// 同时解包foo和bar后才会执行这里
}
}
```
#### 懒加载 (Lazy Initialization)
使用懒加载(`lazy initialization`)来延迟初始化,这是一个很好的特性。如:
```swift
// 对象不需要进行配置
lazy var foo: SomeBigType = SomeBigType()
// 对象需要进行配置时,使用{ ... }()这种形式
lazy var bar: OtherBigType = { [unowned self] in
let obt = OtherBigType()
obt.delegate = self
return obt
}()
```
**注意:**
> 上面代码中使用到了`self`,所以需要使用`[unowned self]`来避免产生循环引用(`retain cycle`)
#### 类型推导 (Type Inference)
对于局部变量,尽量使用类型推导来让代码更紧凑。而对于成员变量来说,应尽量不要使用类型推导来让类的定义更清晰。
**推荐:**
```swift
struct Box: Shape {
var width: Int = 10
var height: Int = 10
var foo: [Int] = []
var bar: [String: Int] = [:]
func scale(rate: Int) {
let width = self.width * rate
let height = self.height * rate
self.width = width
self.height = height
}
}
```
**不推荐:**
```swift
struct Box: Shape {
var width = 10
var height = 10
var foo = [Int]()
var bar = [String: Int]()
func scale(rate: CGFloat) {
let width: Int = self.width * rate
let height: Int = self.height * rate
self.width = width
self.height = height
}
}
```
#### 语法糖 (Syntactic Sugar)
利用语法糖,使用更简短的声明方式:
**推荐:**
```swift
var deviceModels: [String]
var employees: [Int: String]
var faxNumber: Int?
```
**不推荐:**
```swift
var deviceModels: Array<String>
var employees: Dictionary<Int, String>
var faxNumber: Optional<Int>
```
### 内存管理 (Memory Management)
可以通过使用`weak` 和 `unowned`来避免循环引用,但也可以直接使用值类型(`struct`, `enum`)来避免循环引用。
通过`guard let`的形式来产生`strongSelf`:
**推荐:**
```swift
resource.request().onComplete { [weak self] response in
guard let strongSelf = self else {
return
}
let model = strongSelf.updateModel(response)
strongSelf.updateUI(model)
}
```
**不推荐:**
```swift
// 当self的释放先于onComplete回调时,可能引起崩溃
resource.request().onComplete { [unowned self] response in
let model = self.updateModel(response)
self.updateUI(model)
}
```
**不推荐:**
```swift
// 当self的释放介于updateModel()和updateUI()方法之间时,可能会出现意想不到的情况
resource.request().onComplete { [weak self] response in
let model = self?.updateModel(response)
self?.updateUI(model)
}
```
### 访问控制 (Access Control)
一般来说,被标记为`private`、`fileprivate`的属性或方法都应以下划线(`_`)开头。
**推荐:**
```swift
private var _isEnabled: Bool
fileprivate var _isClosed: Bool
private func _foo() {
// code goes here
}
fileprivate func _bar() {
// code goes here
}
```
**不推荐:**
```swift
private var isEnabled: Bool
fileprivate var isClosed: Bool
private func foo() {
// code goes here
}
fileprivate func bar() {
// code goes here
}
```
但例外情况是,当标记被修饰为`private(set)`、`fileprivate(set)`时,不需要下划线(`_`)开头,因为他们都是可访问的属性,只是他们都是只读属性而已,如:
```swift
private(set) var isEnabled: Bool
fileprivate(set) var isClosed: Bool
```
一般将访问控制标记(`access control annotation`)放在声明的最前面,但例外情况是,当有属性标记`@IBAction`、`@IBOutlet`、`@discardableResult`、`@objc`时,需要将属性标记放在最前面。
```swift
private let _message = "Great Scott!"
class TimeMachine {
@IBOutlet private var _lbTitle: UILabel!
@objc private func _foo() -> Bool {
// code goes here
}
}
```
### 控制流 (Control Flow)
优先使用 `for-in` 的方式而不用`while`。
**推荐:**
```swift
for _ in 0..<3 {
print("Hello three times")
}
for (index, person) in attendeeList.enumerated() {
print("\(person) is at position #\(index)")
}
for index in stride(from: 0, to: items.count, by: 2) {
print(index)
}
for index in (0...3).reversed() {
print(index)
}
```
**不推荐:**
```swift
var i = 0
while i < 3 {
print("Hello three times")
i += 1
}
var i = 0
while i < attendeeList.count {
let person = attendeeList[i]
print("\(person) is at position #\(i)")
i += 1
}
```
### 使用Guard语句
通过使用`guard`来避免使用`if`时代码块嵌套过深的问题。
**推荐:**
```swift
func check(phone: String?, name: String?, age: Int) -> Bool {
guard let ph = phone, ph.count > 0 else {
return false
}
guard let nm = name, nm.count > 0 else {
return false
}
guard age > 0 else {
return false
}
return true
}
```
**推荐:**
```swift
func check(phone: String?, name: String?, age: Int) -> Bool {
// 把对phone、name、age的判断放到一个guard里,更简洁
guard
let ph = phone, ph.count > 0,
let nm = name, nm.count > 0,
age > 0 else {
return false
}
return true
}
```
**不推荐:**
```swift
func check(phone: String?, name: String?, age: Int) -> Bool {
if let ph = phone, ph.count > 0 {
if let nm = name, nm.count > 0 {
if age > 0 {
return true
} else {
return false
}
} else {
return false
}
} else {
return false
}
}
```
### 分号 (Semicolons)
在Swift中不要求必须加分号,一般情况下你不需要使用分号。只有当你希望将多行代码写在一行时,才需要加分号来断句。
### 圆括号 (Parentheses)
一般情况下,在类似`if`条件、`for`循环等控制语句中不需要加圆括号。只有当你在进行数学运算时,希望代码可读性更高时才需要适当的添加圆括号。
**推荐:**
```swift
let result = ((x * y) + 2) / h
```
**不推荐:**
```swift
let result = (x * y + 2) / h
```
### 引用
* [swift-style-guide](https://github.com/raywenderlich/swift-style-guide/blob/master/README.markdown)
* [The Swift Programming Language](https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/index.html)
- 说明
- Python编程规范
- Python风格规范
- Python语言规范
- Java编程规范
- 一、命名约定
- 二、常量定义
- 三、格式约定
- 四、OOP约定
- 五、集合处理
- 六、并发控制
- 七、控制语句
- 八、注释约定
- 九、异常日志
- 十、日志约定
- Android开发规范
- 前端开发规范
- HTML
- JavaScript
- CSS
- MySQL约定
- 一、基本规范
- 二、库表设计规范
- 三、字段设计规范
- 四、索引规范
- 五、SQL设计规范
- 六、业务字段命名规范
- 开发安全约定
- 一、代码安全
- 二、移动开发安全
- 三、服务器安全
- 四、安全意识
- 版本管理
- Git使用规范
- 技术实践及可视化
- 一、Code Review
- 二、单元测试
- 三、自动化测试
- 四、技术债
- 五、CI
- IOS开发规范