Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 13db743

Browse files
authoredJan 16, 2025··
[PUBLISHER] Merge #64
* PUSH NOTE : 设计模式-责任链模式&策略模式~扫码场景下的落地.md * PUSH NOTE : 设计模式 - 命令模式&中介者模式&组合模式~AppDelegate 解耦.md * PUSH NOTE : iOS优化-瘦身.md * PUSH NOTE : iOS多线程-Thread.md * PUSH NOTE : iOS多线程-Operation.md * PUSH NOTE : iOS多线程-GCD.md * PUSH NOTE : Xcode常见CLI工具.md * PUSH NOTE : Xcode Tips.md * PUSH NOTE : Xcode Concepts.md * PUSH NOTE : 当Swift中的lazy、weak碰上NSObject.md * PUSH NOTE : 从SIL角度看Swift中的值类型与引用类型.md * PUSH NOTE : Swift开发规范.md * PUSH NOTE : 同层渲染.md * PUSH NOTE : 关于CocoaPods之Nexus、JFrog.md
1 parent 0cf2353 commit 13db743

File tree

3 files changed

+464
-3
lines changed

3 files changed

+464
-3
lines changed
 

‎content/posts/iOS/CocoaPods/关于CocoaPods之Nexus、JFrog.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ description: ""
1010
cover.image: ""
1111
lastmod: 2024-11-21T13:40:07+08:00
1212
---
13-
1413
## 前言
1514

1615
Hi Coder,我是 CoderStar!
Lines changed: 461 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,461 @@
1+
---
2+
title: Swift开发规范
3+
date: 2024-11-16T23:56:02+08:00
4+
categories:
5+
- Swift
6+
tags:
7+
- iOS
8+
- Swift
9+
share: true
10+
description: ""
11+
cover.image: ""
12+
lastmod: 2024-11-27T19:20:00+08:00
13+
---
14+
15+
## 前言
16+
17+
代码的字里行间流淌的是软件生命中的血液,质量的提升是尽可能少踩坑,杜绝踩重复的坑,切实提升质量意识。另外,现代软件架构都需要协同开发完成,高效协作即降低协同成本,提升沟通效率,所谓无规矩不成方圆,无规范不能协作。众所周知,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全。试想如果没有限速,没有红绿灯,谁还敢上路行驶。对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率。-- 摘自《阿里巴巴 Java 代码规范》
18+
19+
该开发规范会持续更新,请关注该 [博文链接](https://coder-star.github.io/iOS/%E8%A7%84%E8%8C%83/Swift%E5%BC%80%E5%8F%91%E8%A7%84%E8%8C%83/)
20+
21+
规约分为【强制】、【推荐】两大类。`说明` 对内容做了引申和解释;`正例` 给出正确的代码示例;`反例` 给出错误的代码示范;
22+
23+
## 命名规约
24+
25+
- 【强制】代码中的命名严禁使用拼音及英文混合的方式,更不允许直接出现中文的方式,最好也不要使用下划线或者美元符号开头;
26+
27+
```swift
28+
反例:_name $name / 学生 / getPingfenByName()[评分]
29+
```
30+
31+
- 【强制】文件名、class、struct、enum、protocol 命名统一使用 UpperCamelCase 风格;
32+
33+
```swift
34+
正例:class LoginName { } / enum SexType { }
35+
反例:class loginName { } / enum SexTYPE { }
36+
```
37+
38+
- 【强制】方法名、参数名、成员变量、局部变量、枚举成员统一使用 lowerCamelCase 风格
39+
40+
```swift
41+
正例:localValue / getMessageInfo()
42+
反例:LocalValue / GetMessageInfo()
43+
```
44+
45+
- 【强制】命名中出现缩略词时,缩略词要么全部大写,要么全部小写,以首字母大小写为准,通用缩略词包括 `JSON``URL``ID` 等;
46+
47+
```swift
48+
正例:class IDUtil {} / func idToString()
49+
反例:class IdUtils {} / func iDToString()
50+
```
51+
52+
- 【强制】不要使用不规范的缩写,力求语义表达完整清楚,能直观的表达意图,不怕名称长;
53+
54+
```swift
55+
正例:class RoundAnimatingButton: UIButton {}
56+
反例:class CustomButton: UIButton {} / AbstractClass 缩写成 AbsClass
57+
```
58+
59+
- 【推荐】全局常量命名使用 k 前缀 + UpperCamelCase 命名;
60+
说明:本质上是不推荐使用全局常量的,主要原因是会散落到代码各处,不方便管理
61+
62+
```swift
63+
正例:kMaxLocaolStoreCount
64+
```
65+
66+
- 【推荐】扩展文件,用 `原始类型名+扩展名` 作为扩展文件名,其中原始类型名及扩展名也使用 UpperCamelCase 风格,如果扩展文件中功能不属于同一类,也可使用 `原生类型名 +Extensions` 的形式;
67+
68+
```swift
69+
正例:UIView+Frame.swift / MessageViewController+Request.swift / UIViewExtensions.swift
70+
```
71+
72+
- 【推荐】工程中文件夹或者 Group 统一使用 UpperCamelCase 风格,一律使用单数形式;
73+
74+
```swift
75+
正例:Resource / Util
76+
```
77+
78+
- 【推荐】文件名如果有复数含义,文件名应使用复数形式,如一些工具类;
79+
80+
```swift
81+
正例:MessageUtils.swift
82+
```
83+
84+
- 【推荐】布尔类型属性使用 `is` 作为属性名前缀,返回值为布尔型类型的方法名使用 `is` 作为方法名作为前缀;
85+
86+
## 定义、修饰规约
87+
88+
- 【强制】不要使用魔法值(即未经定义的常量);
89+
90+
```swift
91+
正例:
92+
let maxDisplayCount = 5
93+
if index == maxDisplayCount {}
94+
反例:
95+
/// 5的含义不明确
96+
if index == 5 {}
97+
```
98+
99+
- 【强制】能用 `let` 修饰的时候,不要使用 `var`
100+
- 【强制】`extension` 上不用加任何修饰符,修饰符加在 `extension` 内的变量或方法上;
101+
说明:目的是当修改 `extension` 中某个方法的访问限制时,不需去考虑外部的 `extension` 访问限制,降低影响面。
102+
103+
```swift
104+
正例:
105+
extension UIView {
106+
public func removeAllSubView() {}
107+
}
108+
反例:
109+
public extension UIView {
110+
func removeAllSubView() {}
111+
}
112+
```
113+
114+
- 【推荐】修饰符顺序按照 注解、访问限制、`static``final` 顺序;
115+
说明:注解是指起始于 @的关键字,如 `@discardableResult、@objc` 等;访问限制是指 `public``private` 等;
116+
117+
```swift
118+
正例:
119+
@objc
120+
public static final func getMessageInfo() {}
121+
```
122+
123+
- 【推荐】考虑方法是否会被重写。如果不会,标记为 `final`
124+
说明:Swift 在编译时会优化 `final` 修饰的方法,派发方式可能由函数表派发优化为直接派发。
125+
- 【推荐】尽可能利用访问限制修饰符控制类、方法等的访问限制,遵循开闭原则;
126+
说明:如确定某方法或变量不应该被外部调用,就使用 `private` 进行修饰,编译程序阻止外部不合适的调用。同时 `private` 在 Swift 中会被隐式加上 `final` 修饰,从而得到优化。善用 `private(set)` 这种方式对读写权限单独控制。并且当范围足够小时,我们后期对其调整的代价就会更小,如果太大,导致外部使用了,后续你就需要考虑更多的兼容。
127+
- 【推荐】表示单例的静态属性,一般命名为 `shared` 或者 `default`,并切记将构造函数私有,否则单例毫无意义;
128+
129+
```swift
130+
正例:
131+
class ApplicationServiceManager {
132+
public static let shared = ApplicationServiceManager()
133+
private init {}
134+
}
135+
```
136+
137+
## 格式规约
138+
139+
- 【强制】类、函数左大括号不另起一行,与名称之间留有空格;
140+
- 【强制】代码中的空格出现地点
141+
- 注释符号与注释内容之间有空格;
142+
- 类继承,参数名和类型之间等,冒号前面不加空格,但后面跟空格;
143+
- 任何运算符前后有空格;
144+
- 表示返回值的 -> 两边;
145+
- 参数列表、数组、元祖、字典里的逗号后面有一个空格;
146+
- 【强制】禁止使用无用分号;
147+
- 【强制】方法之间空一行;
148+
- 【强制】重载的声明放在一起,按照参数的多少从少到多向下排列;
149+
- 【强制】每一行只声明一个常、变量;
150+
- 【强制】如果大括号内为空,直接简写为{},括号之间不需换行;
151+
- 【强制】`if` 后面的 `else\else if`, 跟着上一个 `if\else if` 的右括号;
152+
- 【强制】`switch` 中,`case``switch` 左对齐;
153+
- 【推荐】每行代码长度应小于 100 个字符,或者阅读时候不应该需要滚动屏幕,在正常范围内可以看到完整代码;
154+
- 【推荐】解包时推荐使用原有名字,前提是解包后的名字与解包前的名字在作用域上不会形成冲突;
155+
- 【推荐】实现每个协议时,在单独的 `extension` 里来实现;
156+
- 【推荐】赋值数组、字典时每个元素分别占用一行时,最后一个选项后面也添加逗号;
157+
说明:这样未来如果有元素加入会更加方便;
158+
159+
代码示例(代码不具有业务含义,只是简单的格式规约示例)
160+
161+
```swift
162+
/**
163+
涉及规约
164+
1、类左大括号不另起一行;
165+
2、类继承后跟空格;
166+
*/
167+
168+
/// 格式规约示例
169+
class FormatSample: NSObject {
170+
/**
171+
涉及规约
172+
1、注释符号与注释内容之前有空格;
173+
2、每一行只声明一个变量;
174+
3、不使用分号;
175+
4、注释另起一行,不放在行尾;
176+
5、数组、元祖、字典里的逗号后面有一个空格;
177+
*/
178+
179+
private var resultCode = ""
180+
private var resultArr = ["one", "two"]
181+
private var resultDic = ["key": "name", "value": "张三"]
182+
private var resultTuple = (key: "name", value: "张三")
183+
}
184+
185+
/**
186+
涉及规约
187+
1、方法之间空一行;
188+
2、重载的声明放在一起,按照按照参数的多少从少到多排序;
189+
3、返回值 -> 两遍增加空格;
190+
4、参数名与类型之间空格;
191+
5、如果大括号内为空,则直接简写为{},括号内不换行;
192+
6、if 后面的 else\else if, 跟着上一个 if\else if 的右括号;
193+
7、解包时推荐使用原有名字;
194+
*/
195+
extension FormatSample {
196+
private func logInfo(message: String) {
197+
logInfo(message: message, type: nil)
198+
}
199+
200+
private func logInfo(message: String, type: String?) {
201+
if let type = type {
202+
print("\(type): \(message)")
203+
} else {
204+
print(message)
205+
}
206+
}
207+
208+
private func canLog() -> Bool {
209+
#if DEBUG
210+
return true
211+
#else
212+
return false
213+
#endif
214+
}
215+
216+
/**
217+
涉及规约
218+
1、switch 中, case 跟 switch 左对齐;
219+
*/
220+
private func showSwitchStandardFormat() {
221+
let count = 10
222+
switch count {
223+
case 1:
224+
print(1)
225+
// 如case包含所有情况,可不加default,如遍历枚举类型时
226+
default:
227+
break
228+
}
229+
}
230+
}
231+
```
232+
233+
## 简略规约
234+
235+
- 【强制】Swift 会被结构体按照自身的成员自动生成一个非 public 的初始化方法,如果这个初始化方法刚好适合,不要自己再声明;
236+
237+
```swift
238+
/// 会自动生成 init(name: String) 这样的构造函数,如果符合使用,不要再手动添加该构造函数
239+
struct LoginInfo {
240+
var name: String
241+
}
242+
```
243+
244+
- 【强制】类及结构体初始化方法不要直接调用 `.init`,直接直接省略,使用 ();
245+
246+
```swift
247+
正例:
248+
let loginView = UIView()
249+
反例:
250+
let loginView = UIView.init()
251+
```
252+
253+
- 【强制】如果只有一个 `get` 的计算属性,忽略 `get`
254+
255+
```swift
256+
正例:
257+
var info: String {
258+
return ""
259+
}
260+
反例:
261+
var info: String {
262+
get {
263+
return ""
264+
}
265+
}
266+
```
267+
268+
- 【强制】数据定义时,简单类型尽量使用字面量形式进行自动推断,如果上下文不足以推断字面量类型或者类型比较复杂时,需要声明赋值类型;
269+
270+
```swift
271+
正例:var info = ""
272+
反例:var info: String = ""
273+
```
274+
275+
- 【强制】省略默认的访问权限(internal);
276+
277+
```swift
278+
正例:var info = ""
279+
反例:internal var info = ""
280+
```
281+
282+
- 【强制】使用枚举属性时使用自动推断,进行缩写;
283+
284+
```
285+
enum Sex {
286+
case male
287+
case female
288+
}
289+
正例:let sex: Sex = .male
290+
反例:let sex: Sex = Sex.male
291+
```
292+
293+
- 【强制】switch-case 里不用显式添加 `break`;
294+
295+
```swift
296+
let count = 10
297+
switch count {
298+
case 1:
299+
print(1)
300+
// 此处不用显式添加break,Swift中每个case都会默认break。
301+
}
302+
```
303+
304+
- 【强制】访问实例成员或方法时不要使用 `self.`,特殊场景除外,如构造函数,闭包内;
305+
306+
```swift
307+
class LoginInfo {
308+
func log() {}
309+
310+
func recordInfo() {
311+
/// 正例
312+
log()
313+
314+
/// 反例
315+
self.log()
316+
}
317+
}
318+
```
319+
320+
- 【强制】当方法无返回值时,不需添加 `void`
321+
322+
```swift
323+
正例:func getMessageInfo() {}
324+
反例:func getMessageInfo() -> Void {}
325+
```
326+
327+
- 【强制】无用的代码及时删除;
328+
**说明:可能有开发者觉得有些代码可能后续会用到,所以采取了注释的方式。但是这种方式很容易演变成代码会一直放在那,永远不会删掉。即使觉得后续会用到,也请及时删除掉,不然 Git 留着干什么用呢?**
329+
- 【推荐】使用闭包时,尽量使用最简写,如优先使用尾随闭包等;
330+
- 【推荐】过滤,转换等,优先使用 filter, map 等高阶函数简化代码,并尽量使用最简写;
331+
- 【推荐】**尽量**使用各种语法糖;
332+
说明:语法糖一定程度上会降低代码的可度性,尽量这个度根据各自公司进行调整。
333+
- 【推荐】类似注解的修饰词单独占一行,如 `@objc``@discardableResult` 等;
334+
- 【推荐】如果 for 循环在函数体中只有一个 if 判断,使用 `for where` 进行替换;
335+
336+
## 注释、可读规约
337+
338+
- 【强制】文档(API)注释使用单行注释,即 `///`,不使用多行注释,即 `/** */`。 多行注释用于对某一代码段、设计或者复杂业务进行描述;
339+
- 【强制】对于公开的类、方法以及属性等必须加上文档(API)注释,方法需要加上对应的 `Parameter(s)``Returns``Throws` 等标签,建议使用 `⌥ ⌘ /` 自动生成文档模板;
340+
- 【强制】将注释放在代码上一行,而不是放在代码后;
341+
说明:放在代码后有两个弊端,一是当代码稍微长一点后,注释可能需要横向滚动后才能看全;另一个弊端是,当代码修改,极易将注释删除,或者由于后面有注释,前面的代码修改起来有些许不方便。
342+
- 【推荐】在代码中灵活的使用一些地标注释,如 `MARK``FIXME``TODO`,当同一文件中存在多种类型定义或者多种逻辑时,可以使用 `Mark` 进行分组注释,方便通过 `Xcode` 顶部面包屑进行切换;
343+
- 【推荐】实现每个协议时,尽量在单独的 extension 里来实现;
344+
代码示例:
345+
346+
```swift
347+
// MARK: - View子视图操作相关
348+
349+
extension UIView {
350+
/// 同时添加多个视图
351+
/// - Parameter subviews: 子View可变参数
352+
public func addSubviews(_ subviews: UIView...) {
353+
subviews.forEach(addSubview)
354+
}
355+
356+
/// 移除所有子View
357+
public func removeAllSubview() {
358+
subviews.forEach {
359+
$0.removeFromSuperview()
360+
}
361+
}
362+
}
363+
```
364+
365+
## 编译效率规约
366+
367+
该段是提高编译效率,减少编译时间总结提出的规约,对代码可读性及统一风格等影响不大,所以该部分只是作为推荐,不强制一定遵守。
368+
369+
- 【推荐】数组合并建议使用 append 方法而不是 + 号拼接;
370+
371+
```swift
372+
var resultArr = ["1", "2"]
373+
let extraArr = ["3", "4"]
374+
正例:resultArr.append(contentsOf: extraArr) / let newArray = [resultArr, extraArr].joined()
375+
反例:resultArr += extraArr / let newArray = resultArr + extraArr
376+
```
377+
378+
- 【推荐】字符串合并避免使用 + 号而是多采用 `"\(str1)\(str2)"` 的形式;
379+
380+
```swift
381+
let code = "200"
382+
let message = "success"
383+
正例:let result = "\(code)\(message)"
384+
反例:let result = code + message
385+
```
386+
387+
- 【推荐】if 的条件部分不要做过多运算
388+
389+
```swift
390+
正例:if count == 900 {}
391+
反例:if count == 60 * 60 / 2 / 2 {}
392+
```
393+
394+
- 【推荐】不要让可选值使用?? 赋默认值再嵌套其他运算;
395+
- 【推荐】将长计算式代码拆分,最后组合计算;
396+
- 【推荐】尽量不使用 `Storyboard` 或者 `Xib`,会增加编译时间;
397+
还存在一些问题可见 [think-twice-before-scaling-your-app-with-interface-builder](https://craft.faire.com/think-twice-before-scaling-your-app-with-interface-builder-90214ebdb12a)
398+
- 【推荐】减少三目运算符的使用;
399+
400+
## 优化规约
401+
402+
- 【强制】函数参数数量最多不得超过 8 个;
403+
说明:寄存器数目问题,超过 8 个会影响效率;
404+
- 【强制】避免强制解包以及强制类型映射,尽量使用 `if let``guard let` 进行解包,禁止 `try!` 形式处理异常,避免使用隐式解包;
405+
- 【强制】避免判断语句嵌套层次太深,使用 guard 提前返回;
406+
- 【推荐】在闭包中使用 self 时使用捕获列表 `[weak self]` 避免循环引用,闭包开始判断 self 的有效性;
407+
408+
```swift
409+
正例:
410+
timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
411+
guard let self = self else { return }
412+
self.timerHandle()
413+
}
414+
```
415+
416+
- 【推荐】使用委托和协议时,避免循环引用,定义属性的时候使用 `weak` 修饰;
417+
- 【推荐】能用 `struct` 解决的,尽量使用 `struct` 而不是 `class`
418+
说明:`struct` 属于值类型,使用其有很多好处:效率高、不需担心循环引用问题、线程安全等。
419+
- 【推荐】对 String 判空时优先采用 `isEmpty`
420+
说明:Swift 里面的 String 的 `index``count` 不是一一对应的(兼容 `Unicode`),所以 `stirng.count == 0` 的效率不如 `string.isEmpty`[Why using isEmpty is faster than checking count == 0](https://www.hackingwithswift.com/articles/181/why-using-isempty-is-faster-than-checking-count-0)
421+
- 【推荐】如果协议只会被类使用,建议加上 `AnyObject` 限制;
422+
说明:编译器可以对此进行优化,其可以明确在处理一个类,继而进行引用计数,而不用考虑结构体以及其带来的额外的逻辑及结构。
423+
424+
```swift
425+
正例:protocol Pingable: AnyObject { func ping() -> Int }
426+
```
427+
428+
-
429+
430+
## 设计规约
431+
432+
- 推荐善用 `func` 嵌套;
433+
说明:比如一段逻辑需要在方法内复用,可以在方法中定义方法,比如闭包中以及正常方法体中需要执行同一段逻辑;
434+
- 推荐委托性质的协议,第一个参数应尽量保证为代理源,类似 `UITableViewDatasoure`;
435+
- 推荐在方法设计时,其需要返回值,但是大部分情况业务方不需要使用返回值,可使用 `@discardableResult` 来对该方法进行标注,防止出现大量 `Warning`;
436+
- 推荐尽可能少的使用全局命名空间,如常量变量方法等;
437+
- 推荐优先创建函数而不是自定义操作符;
438+
- 推荐Swift 中的协议和泛型十分强大,设计时应优先考虑面向协议编程;
439+
- 推荐尽量不使用 `Bool?` 或者 `Array?` 这两者类型,会造成使用模糊
440+
441+
## 其他
442+
443+
- 强制图形化的字面量,`#colorLiteral(...)`, `#imageLiteral(...)` 只能用在 playground 当做自我练习使用,禁止在项目工程中使用,这会让维护者在后期维护时无法第一眼了解到颜色的 hex 值或者图片的名称
444+
说明:图形化的字面量不仅不方便直观的查看其颜色值或者图片名字,也不利于颜色图片统一配置管理
445+
446+
## 工具
447+
448+
[SwiftLint 工具](https://github.com/realm/SwiftLint) 提示格式错误
449+
450+
[SwiftFormat 工具](https://github.com/nicklockwood/SwiftFormat) 提示并修复格式错误
451+
452+
两者大部分格式规范都是一致的,少许规范不一致,两个工具之间使用不冲突,可以在项目中共存我们通过配置文件可以控制启用或者关闭相应的规则,具体使用规则参照对应仓库的 REAMME.md 文件
453+
454+
## 相关规范
455+
456+
- [Swift 官方 API 设计指南](https://swift.org/documentation/api-design-guidelines/)
457+
- [Google 发布的 Swift 编码规范](https://google.github.io/swift/#apples-markup-format)
458+
- [linkedin编码规范](https://github.com/linkedin/swift-style-guide)
459+
- [raywenderlich编码规范](https://github.com/raywenderlich/swift-style-guide)
460+
- [airbnb编码规范](https://github.com/airbnb/swift)
461+
- [swift-best-practices](https://github.com/Lickability/swift-best-practices)

‎content/posts/iOS/Swift/当Swift中的lazy、weak碰上NSObject.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,9 @@ final public class Lazy<T> {
129129

130130
```swift
131131
class MyClass: NSObject, MyServiceDelegate {
132-
private lazy var service = Lazy<MyService> {
133-
let service = MyService()
132+
private lazy var service = Lazy<MyService> { [weak self] in
133+
guard let self = self else { return MyService() }
134+
let service = MyService()
134135
service.delegate = self
135136
return service
136137
}

0 commit comments

Comments
 (0)
Please sign in to comment.