SwiftLint 的理解和使用

目的:

? iOS APP在下一個(gè)版本會(huì)用swift開發(fā),在多人開發(fā)中,即使有官網(wǎng)的規(guī)范模板.但每個(gè)人的代碼風(fēng)格和規(guī)范難以做到完全一致,對后期項(xiàng)目維護(hù)會(huì)增加一定的困難,這里主要是對一個(gè)規(guī)范代碼風(fēng)格工具swiftlint使用的一個(gè)評估。

前言:

隨著項(xiàng)目的擴(kuò)大,依靠人工codereview來保證項(xiàng)目的質(zhì)量,越來越不現(xiàn)實(shí),這時(shí)就有必要借助于一種自動(dòng)化的代碼審查工具:程序靜態(tài)分析。

程序靜態(tài)分析(Program Static Analysis)是指在不運(yùn)行代碼的方式下,通過詞法分析、語法分析、控制流、數(shù)據(jù)流分析等技術(shù)對程序代碼進(jìn)行掃描,驗(yàn)證代碼是否滿足規(guī)范性、安全性、可靠性、可維護(hù)性等指標(biāo)的一種代碼分析技術(shù)。(來自百度百科)

介紹:

SwiftLint 是 realm 公司開發(fā)的一個(gè)插件,專門用于管理 Swift 代碼的規(guī)范。Swift 是 Apple 在 2014 年推出的用于 Apple 系列產(chǎn)品開發(fā)的新一代編程語言,目標(biāo)是取代原來的 Object-C . 和其他編程語言,比如 C、Java、Python 等一樣,Swift 中也規(guī)定了基本的編碼規(guī)范,用于定義采用 Swift 編程時(shí), 對于「美」的傾向?;緲?biāo)準(zhǔn)已經(jīng)在git 開源Swift 編程官網(wǎng)規(guī)范。realm 公司開發(fā) SwiftLint 插件,就是用來規(guī)范開發(fā)者在編程時(shí)對 Swift 規(guī)范進(jìn)行貫徹執(zhí)行。

這里簡要介紹下 realm 公司。它主要的開發(fā)產(chǎn)品是用于移動(dòng)端的數(shù)據(jù)庫,可以跨多個(gè)現(xiàn)有的移動(dòng)端系統(tǒng)(Android/iOS 等),API 簡單易用,而且效率較高, 號(hào)稱現(xiàn)在擁有 10 億用戶。它誕生于美國著名孵化器 Y combinator (就是年初從百度辭職的副董事長陸奇新加入的公司)項(xiàng)目。

SwiftLint 的工作原理是檢查 Swift 代碼編譯過程中的 AST 和 SourceKit 環(huán)節(jié),從而可以擺脫不同版本 Swift 語法變化的影響。AST 是編譯前端形成的抽象語法書(Abstract Symbolic Tree), SourceKit 過程用來對 AST 進(jìn)行代碼優(yōu)化,減少內(nèi)存開銷,提高執(zhí)行效率。如果對編譯過程理解不太清楚,可以參考:ASTLLVM優(yōu)點(diǎn)

SwiftLint 是一個(gè)用于強(qiáng)制檢查 Swift 代碼風(fēng)格和規(guī)定的一個(gè)工具。它的實(shí)現(xiàn)是 Hook 了 Clang 和 SourceKit 從而能夠使用AST來表示源代碼文件的更多精確結(jié)果。Clange我們了解了,那SourceKit是干什么用的?

SourceKit包含在Swift項(xiàng)目的主倉庫,它是一套工具集,支持Swift的大多數(shù)源代碼操作特性:源代碼解析、語法突出顯示、排版、自動(dòng)完成、跨語言頭生成等工作。

安裝使用:

安裝:

1:可以使用homebrew進(jìn)行全局安裝:

需要在已經(jīng)安裝了homebrew 前提下:

打開終端輸入:

brew install SwiftLint?

如果已安裝SwiftLint ,可更新至當(dāng)前最新版本:0.40.1

更新命令:

brew upgrade?swiftlint

查看當(dāng)前版本號(hào)

$ swiftlint version

//查看所有命令

$ swiftlint help

//忽略空格導(dǎo)致的警告和錯(cuò)誤

$ swiftlint autocorrect

//輸出所有的警告和錯(cuò)誤

$ swiftlint lint

//查看所有可獲得的規(guī)則以及對應(yīng)的 ID

$ swiftlint rules

如果我們想將此次分析生成一份報(bào)告,也是可以的(該命令是通過homebrew安裝的swiftlint

# reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)

$ swiftlint lint--reporter html>swiftlint.html

2:使用cocoaPods安裝:

pod?’SwiftLint’,這個(gè)方式可以安裝不同版本的SwiftLint但是只能針對單個(gè)項(xiàng)目.

pod?'SwiftLint','0.39.2' 或者指定調(diào)用版本;

3:SwiftLint支持pkg安裝包安裝.

使用:

安裝完成后,需要在Xcode中的Build Phases新建一個(gè)?Run Script Phase 配置項(xiàng)添加腳本,

?3.1:全局安裝添加腳本

if which swiftlint >/dev/null; then

? swiftlint

else

? echo "warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint"

fi

3.2:CocoaPods安裝添加腳本

"${PODS_ROOT}/SwiftLint/swiftlint"

這個(gè)腳本在安裝了swiftlint后每次運(yùn)行都會(huì)執(zhí)行,如果沒有安裝swiftlint,會(huì)警告異常或者報(bào)錯(cuò).

3.3:.swiftlint.yml:

這是一個(gè)配置文件,可以通過這個(gè)配置文件來自定義規(guī)則或者修改默認(rèn)規(guī)則.

這里是自用的一個(gè).swiftlint.yml ?配置內(nèi)容

excluded:?# 執(zhí)行 linting 時(shí)忽略的路徑。 優(yōu)先級(jí)比 `included` 更高。

-?Pods

disabled_rules:?#執(zhí)行時(shí)排除掉的規(guī)則

-?identifier_name?#命名規(guī)則必須按照駝峰原則,與后臺(tái)傳的Json字段命名沖突,建議排除掉

-?trailing_whitespace?#每一個(gè)空行不能有空格,會(huì)與Xcode換行后自動(dòng)對齊生成的空格沖突,建議排除掉

-?vertical_whitespace?#垂直方向上的空格行,限制為一行(注釋除外)

-?trailing_newline?#末尾空行,文件末尾應(yīng)該有一個(gè)空行

-?unused_closure_parameter?#函數(shù)的參數(shù)必須被使用

-?class_delegate_protocol?#delegate protocol 應(yīng)該被設(shè)定為 class-only,才能被弱引用

-?weak_delegate?#代理要設(shè)置為弱引用和上面的

-?control_statement?#if while 等判斷條件不要用括號(hào) 括起來,另外注意條件出的空格

-?leading_whitespace?#文件末尾不應(yīng)該存在空格符

-?statement_position?#else and catch 應(yīng)該與 } 在同一行,以空格間隔

-?orphaned_doc_comment?#注釋要寫在聲明中

-?type_body_length?#類型體長度。類型體長度不應(yīng)該跨越太多行,超過200行給warning,超過350行給error,可自定義enum或struct

-?notification_center_detachment?#移除通知要在 'deinit'中

# variable_name與identifier_name沖突,啟用后identifier_name需關(guān)閉,否則build不通過。

# - variable_name # 變量名應(yīng)該只包含字符數(shù)字字符, 并且只能以小寫字母開頭或者應(yīng)該只包含大寫字母。此外,當(dāng)變量名被聲明為static(靜態(tài))變量時(shí)或者immutable不可變的時(shí)候,這時(shí)或許可以以一個(gè)大寫字母開頭。最后,變量名不應(yīng)該太長或者太短(應(yīng)該在3-40個(gè)字符間,否則會(huì)觸發(fā)警告?。。?。注意:目前變量名只適用于自己寫的方法的參數(shù)和自己寫的class中的全局常量或變量, 對于系統(tǒng)自帶的方法里面和自己寫的方法里面沒有作用

-?multiple_closures_with_trailing_closure?#當(dāng)函數(shù)有多個(gè)閉包時(shí), 不建議使用尾隨閉包語法/多個(gè)閉包與尾隨閉包沖突:在傳遞多個(gè)閉包參數(shù)時(shí)不應(yīng)該使用尾隨關(guān)閉語法。

-?trailing_comma?# 數(shù)組最后一個(gè)元素后面有空格

-?file_length?#文件長度

-?for_where?#使用 `for where` 代替 簡單的 `for { if }`

-?unneeded_break_in_switch?# 在switch-case語句中, 有方法調(diào)用或操作時(shí),避免使用break語句

-?large_tuple?#元祖成員 元組沖突:元組應(yīng)該最多有2個(gè)成員,多余兩個(gè)會(huì)報(bào)錯(cuò)

-?redundant_string_enum_value?#在定義字符串枚舉的時(shí)候, 當(dāng)字符串枚舉值等于枚舉名稱時(shí),可以不用賦值

-?syntactic_sugar?#語法糖[Int] 代替Array / 例:要使用 [] ? 等數(shù)組字典可選項(xiàng)的語法糖

-?line_length?# 行的字符長度,這個(gè)強(qiáng)烈不推薦使用。官方的規(guī)定是超過120字符就給warning,

# 函數(shù)參數(shù)計(jì)數(shù)違例:函數(shù)應(yīng)該有5個(gè)參數(shù),多余會(huì)報(bào)錯(cuò) 函數(shù)參數(shù)個(gè)數(shù), 函數(shù)參數(shù)數(shù)量(init方法除外)應(yīng)該少點(diǎn),不要太多,swiftlint規(guī)定函數(shù)參數(shù)數(shù)量超過5個(gè)給warning, 超過8個(gè)直接報(bào)error。這個(gè)屬性推薦使用,由于簡單就不舉例了。

#注:function_parameter_count: error 這樣并不能改變它的警告或錯(cuò)誤,該屬性不允許修改,但是可以禁用

-?function_parameter_count?#函數(shù)參數(shù)個(gè)數(shù) 默認(rèn)5 warning 8 error

-?operator_whitespace?#當(dāng)定義空格操作符的時(shí)候,被定義的名字或類型兩邊應(yīng)該各有一個(gè)單行空格操作符

-?closure_parameter_position?#閉包參數(shù)位置, 閉包參數(shù)應(yīng)該 { 左邊在同一行

-?unused_capture_list?#閉包中沒有被使用的參數(shù)應(yīng)該刪除

-?redundant_void_return?#在不必要的時(shí)候, 不需要寫 ->() and -> Void

-?mark?# //MARK: - 正確使用 mark 的格式 `// MARK: - message`

-?redundant_optional_initialization?# 默認(rèn)值賦值為nil

-?return_arrow_whitespace?# -> 前后要有空格,函數(shù)定義返回的 -> 前后有空格, 不換行

-?unused_optional_binding?#在使用if判斷某變量是否為nil的時(shí)候, 不建議使用下劃線(_)

-?vertical_parameter_alignment?#函數(shù)參數(shù)分為多行書寫的時(shí)候, 頭部(小括號(hào)后面一位)必須對齊

-?number_separator?#使用 _ 分割大數(shù), 讓數(shù)字更清晰

opt_in_rules:?# some rules are only opt-in 一些規(guī)則僅僅是可選的

-?opening_brace?# 右括號(hào)之前應(yīng)有一個(gè)空格,并與聲明在同一行

-?unused_import?# import 的文件要被使用 All imported modules should be required to make the file compile.

-?comma?#逗號(hào)? [a, b, c, d] 后面必須有一個(gè)空格, 前面不要空格 --->項(xiàng)目中掃描到不少,待調(diào)整

force_unwrapping:?error?#避免強(qiáng)制解包

cyclomatic_complexity:?error?#代碼復(fù)雜度,默認(rèn)為10,循環(huán)復(fù)雜度。函數(shù)體的復(fù)雜度的限制,這個(gè)屬性主要約束條件句、循環(huán)句中的循環(huán)嵌套問題, 當(dāng)嵌套太多的循環(huán)時(shí),則會(huì)觸發(fā)swiftlint中的warning和error,當(dāng)達(dá)到10個(gè)循環(huán)嵌套時(shí)就會(huì)報(bào)warning,達(dá)到20個(gè)循環(huán)嵌套時(shí)就會(huì)報(bào)error,強(qiáng)烈推薦這個(gè)屬性。嵌套太多,可讀性差!

trailing_semicolon:?error?#末尾跟分號(hào)

legacy_constructor:?error?#使用 swift 提供的 struct 構(gòu)造函數(shù), 避免使用 遺留的構(gòu)造函數(shù) 比如 CGPointMake(10, 10)

empty_count:?error?#建議使用isEmpty判斷,而不是使用count==0判斷

prohibited_interface_builder:?error?#禁止用interface Builder 創(chuàng)建視圖

function_body_length:?error?#函數(shù)體長度 默認(rèn)超過40行warning,超過100行直接報(bào)錯(cuò)。推薦使用。

type_name:?error?#類型名應(yīng)該只包含字母數(shù)字字符, 并且以大寫字母開頭,長度在3-40個(gè)字符

shorthand_operator:?error?# 使用+= , -=, *=, /=?222222 代替 a = a + 1

implicit_getter:?error?# read-only參數(shù)不應(yīng)該有g(shù)etter方法

nesting:?error?#類型定義嵌套不要超過1層 , 聲明嵌套不要超過5層

private_unit_test:?error?#單元測試方法 不能設(shè)置為 private

unused_enumerated:?error?#默認(rèn)-當(dāng)參數(shù)沒有被全部使用的時(shí)候, 不要使用容器的 enumerated 方法

valid_ibinspectable:?error?#默認(rèn)-IBInspectable 必須是可變參數(shù)

explicit_type_interface:?error?#需要跑明確參數(shù)的類型定義

fatal_error_message:?error?#fatalError 必須擁有一個(gè) message

first_where:?error?#使用 `.first(where:)` 代替 `.filter { }.first`

implicitly_unwrapped_optional:?error?#避免隱式解析可選類型的使用 / 避免隱式解包(定義 ! 類型)

switch_case_on_newline:?error?#switch 的 case 需要新啟一行

object_literal:?error?#避免 image and color 使用字面量初始化, 需要把相關(guān)圖片名,顏色RGB 等參數(shù)定義為 enum struct 或者常量

overridden_super_call:?error?#override 方法需要調(diào)用 super method

private_action:?error?#IBActions應(yīng)該是私有的

private_outlet:?error?#IBOutlets 應(yīng)該設(shè)置為 private, 來避免泄露

prohibited_super_call:?error?#某些特殊的 override 方法, 禁止調(diào)用 super method

redundant_nil_coalescing:?error?#避免使用 `object ?? nil`

explicit_init:?error?#避免直接調(diào)用 init 方法

operator_usage_whitespace:?error?#操作符需要使用一個(gè)空格間隔

nimble_operator:?error?#避免 expect 一個(gè)確定的判斷

attributes:?error?#Attributes 針對類和func重啟一行, 針對變量在同一行

duplicate_imports:?error?#重復(fù)導(dǎo)入

convenience_type:?error?#用于檢測靜態(tài)成員的類型應(yīng)實(shí)現(xiàn)為無大小寫的枚舉,以避免實(shí)例化

explicit_enum_raw_value:?error?#枚舉應(yīng)設(shè)置默認(rèn)值

file_name_no_space:?error?#文件名不應(yīng)包含任何空格

ibinspectable_in_extension:?error?#擴(kuò)展不應(yīng)添加@IBInspectable屬性

identical_operands:?error?#比較兩個(gè)相同的操作數(shù)可能是一個(gè)錯(cuò)誤

lower_acl_than_parent:?error?#確保定義的訪問控制級(jí)別低于其父級(jí)

missing_docs:?error?#聲明應(yīng)記錄在案

no_extension_access_modifier:?error?#禁止使用擴(kuò)展訪問修飾符,例如不建議:private extension String {}

no_grouping_extension:?error?#擴(kuò)展名不應(yīng)用于對同一源文件中的代碼進(jìn)行分組 不推薦如下注釋寫法

# class Ham { class Spam {}}

# ↓extension Ham.Spam {}

optional_enum_case_matching:?error?#將枚舉大小寫與不帶'?'的可選枚舉匹配 在Swift 5.1及更高版本中受支持。

prefixed_toplevel_constant:?error?#頂級(jí)常量的前綴應(yīng)為k

quick_discouraged_call:?error?#不鼓勵(lì)在“describe”和/或“context” 框中進(jìn)行調(diào)用。

#| identifier | opt-in | correctable | enabled in your config

#----------------------+

# | 以下是默認(rèn)規(guī)則 swiftlint版本:0.40.1 默認(rèn):warning 少數(shù)是:error

# | empty_parameters | no | yes | yes 使用 `() -> ` 代替 `Void ->

# | empty_parentheses_with_trailing_closure | no | yes | yes 尾閉包避免空參數(shù)括號(hào)

# | colon no | yes | yes 冒號(hào)左邊沒有空格, 右邊有且只有一個(gè)空格

# | comma no | yes | yes 逗號(hào)左邊沒有空格, 右邊有空格

# | legacy_cggeometry_functions | no | yes | yes 避免使用 C 風(fēng)格 的 CG 遺留函數(shù), 使用 struct extension

# | legacy_constant | no | yes | yes 避免使用 遺留的全局常量, 使用 struct 內(nèi)定義的 常量

# | legacy_constructor | no | yes | yes 使用 swift 提供的 struct 構(gòu)造函數(shù), 避免使用 遺留的構(gòu)造函數(shù) 比如 CGPointMake(10, 10)

# | legacy_nsgeometry_functions | no | yes | yes 避免使用 C 風(fēng)格 的 NS 遺留函數(shù), 使用 struct extension

# | opening_brace | no | yes | yes 需要正確書寫大括號(hào)格式

# | redundant_discardable_let | no | yes | yes 使用 `_ = foo()` 代替 `let _ = foo()`

# | redundant_optional_initialization | no | yes | yes 不需要寫默認(rèn)值為 nil

# | closing_brace | no | yes | yes 小括號(hào)內(nèi)包含函數(shù)(大括號(hào))的時(shí)候,之間沒有空格

# | redundant_void_return | no | yes | yes 在不必要的時(shí)候, 不需要寫 ->() and -> Void

# | return_arrow_whitespace | no | yes | yes 函數(shù)定義返回的 -> 前后有空格, 不換行

# | statement_position | no | yes | yes else and catch 應(yīng)該與 } 在同一行, 以空格間隔

# | trailing_newline | no | yes | yes 文件末尾應(yīng)該有一個(gè)空行

# | trailing_semicolon | no | yes | yes 行末尾不加分號(hào),warning

# | trailing_whitespace | no | yes | yes 行末尾不加空格

# | unused_closure_parameter | no | yes | yes 函數(shù)的參數(shù)必須被使用

# | vertical_whitespace | no | yes | yes 不能有連續(xù)多個(gè)空行

# | void_return | no | yes | yes 使用 `-> Void` 代替 `-> ()

# | compiler_protocol_init | no | no | yes 不應(yīng)該直接調(diào)用字面量轉(zhuǎn)換的初始化方法,諸如編譯器協(xié)議中聲明的初始化程序ExpressibleByArrayLiteral不應(yīng)直接調(diào)用

# | control_statement | no | no | yes if while 等判斷條件不要用括號(hào) 括起來

# | cyclomatic_complexity | no | no | yes 不應(yīng)該存在太復(fù)雜的函數(shù)(判斷語句過多)

# | discarded_notification_center_observer | no | no | yes 當(dāng)使用 block 注冊通知中心 observer 的時(shí)候, 應(yīng)該存儲(chǔ)函數(shù)返回的 observer, 以便之后的刪除

# | force_cast | no | no | yes error:避免強(qiáng)制的類型轉(zhuǎn)化,這里表示強(qiáng)解類型警告 as! Int

# | force_try | no | no | yes error:對會(huì)拋出異常(throws)的方法,不建議try,強(qiáng)解, 避免 `try!`

# | function_body_length | no | no | yes body 長度限制 warning: 40, error: 100

# | generic_type_name | no | no | yes 類型命名規(guī)則限制,以大寫字母開頭,且長度在1到20個(gè)字符之間,(min_length) w/e: 1/0, (max_length) w/e: 20/1000, excluded: [], allowed_symbols: [], validates_start_with_lowercase: true

# | implicit_getter | no | no | yes read-only 參數(shù)不應(yīng)該有 getter

# | nesting | no | no | yes 類型定義嵌套不要超過1層 , 聲明嵌套不要超過5層

# | private_unit_test | no | no | yes 單元測試方法 不能設(shè)置為 private

# | redundant_string_enum_value | no | no | yes 字符串類型枚舉, 會(huì)有默認(rèn) string 值,與名字相同, 不要再次設(shè)置

# | shorthand_operator | no | no | yes 使用 +=, -=, *=, /=

# | syntactic_sugar | no | no | yes 要使用 [] ? 等數(shù)組字典可選項(xiàng)的語法糖

# | todo no | no | yes error:避免 TODOs and FIXMEs 標(biāo)識(shí)

# | trailing_comma | no | no | yes 數(shù)組末尾不要加空格,warning mandatory_comma: false

# | type_body_length | no | no | yes 類型體行數(shù)限制

# | type_name | no | no | yes 類型名字限制規(guī)則,類型名稱只能包含字母數(shù)字字符,以大寫字母開頭,并且長度在3到40個(gè)字符之間, (min_length) w/e: 3/0, (max_length) w/e: 40/1000, excluded: [], allowed_symbols: [], validates_start_with_lowercase: true

# | unused_enumerated | no | no | yes error:當(dāng)參數(shù)沒有被全部使用的時(shí)候, 不要使用容器的 enumerated 方法

# | unused_optional_binding | no | no | yes 必須使用定義的 optional binding

# | valid_ibinspectable | no | no | yes error: IBInspectable 必須是可變參數(shù)

# | vertical_parameter_alignment | no | no | yes 函數(shù)參數(shù)分為多行書寫的時(shí)候, 頭部(小括號(hào)后面一位)必須對其

# | weak_delegate | no | no | yes delegate 應(yīng)該被設(shè)置為 weak

# | block_based_kvo | no | no | yes 計(jì)算的屬性和下標(biāo)中的Getter和setter應(yīng)該保持一致的順序。

# | discouraged_direct_init | no | no | yes 不鼓勵(lì)直接初始化并聲明的類型 warning:types: ["Bundle", "Bundle.init", "UIDevice", "UIDevice.init"]

# | duplicate_enum_cases | no | no | yes error:枚舉不能設(shè)置兩個(gè)或者以上相同的名字

# | duplicate_imports | no | no | yes 重復(fù)導(dǎo)入

# | dynamic_inline | no | no | yes error:避免同時(shí)使用'dynamic'和'@inline(__ always)'

# | empty_enum_arguments | no | yes | yes 如果將枚舉與關(guān)聯(lián)的類型匹配(如果不使用),則可以忽略參數(shù)

# | inert_defer | no | no | yes 如果defer在其父范圍的末尾,則無論如何它都會(huì)被執(zhí)行

# | is_disjoint | no | no | yes 優(yōu)先:Set.isDisjoint(with:) 不建議:Set.intersection(_:).isEmpty

# | legacy_hashing | no | no | yes hash(into:)優(yōu)先使用函數(shù)而不是覆蓋hashValue

# | no_fallthrough_only | no | no | yes 僅當(dāng)case包含至少一個(gè)其他語句時(shí),才能使用穿透。

# | no_space_in_method_call | no | yes | yes error:不要在方法名稱和括號(hào)之間添加空格

# | nsobject_prefer_isequal | no | no | yes NSObject子類應(yīng)實(shí)現(xiàn)isEqual而不是==

# | private_over_fileprivate | no | yes | yes 推薦:private 不建議:fileprivate; warning:validate_extensions: false

# | protocol_property_accessors_order | no | yes | yes error:在協(xié)議中聲明屬性? 要按順序先寫 get set方法

# | reduce_boolean | no | no | yes 優(yōu)先使用.allSatisfy()或.contains() 不建議使用:reduce(true)或reduce(false)

# | redundant_objc_attribute | no | yes | yes Objective-C屬性(@objc)在聲明中是多余的,warning

# | redundant_set_access_control | no | no | yes 如果屬性設(shè)置程序訪問級(jí)別與變量訪問級(jí)別相同,則不應(yīng)明確

# | superfluous_disable_command | no | no | yes 當(dāng)禁用規(guī)則不會(huì)在禁用區(qū)域觸發(fā)違規(guī)時(shí),SwiftLint的“禁用”命令是多余的。如果要記錄命令,請使用“-”,warning

# | switch_case_alignment | no | no | yes Case語句應(yīng)與其封閉的switch語句垂直對齊,如果沒有其他配置,則縮進(jìn)

# | unneeded_notification_center_removal | no | no | yes 觀察者會(huì)自動(dòng)在dealloc(iOS 9 / macOS 10.11)上取消注冊,因此您不應(yīng)調(diào)用removeObserver(self)deinit

# | unused_control_flow_label | no | yes | yes 未使用的控制流標(biāo)簽應(yīng)被刪除,warning

# | unused_import | yes | yes | yes 嚴(yán)重警告, 所以導(dǎo)入的類文件需要編譯 severity: warning, require_explicit_imports: false, allowed_transitive_imports: []

# | unused_setter_value | no | no | yes 不使用設(shè)定值

# | xctfail_message | no | no | yes XCTFail調(diào)用應(yīng)包括斷言的描述,描述不能為空

# | 以下是可選規(guī)則, 需要額外添加

# +------------------------------------------+--------+-------------+------------------------+-------------+----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------+

# | identifier | opt-in | correctable | enabled in your config | kind | analyzer | configuration

# | anyobject_protocol | yes | yes | no | lint | no | warning:對于純類協(xié)議,建議AnyObject 不推薦 class

# | array_init | yes | no | no | lint | no | warning:推薦使用 Array(seq) 不推薦語法: seq.map { $0 } 將序列轉(zhuǎn)換為Array。

# | attributes | yes | no | no | style | no | warning, always_on_same_line: ["@IBAction", "@NSManaged"], always_on_line_above: []

# | class_delegate_protocol | no | no | no | lint | no | warning delegate protocol 應(yīng)該被設(shè)定為 class-only,才能被弱引用

# | closure_body_length | yes | no | no | metrics | no | warning: 20, error: 100 封閉體不應(yīng)跨越太多行

# | closure_end_indentation | yes | yes | no | style | no | warning 閉包前后縮進(jìn)應(yīng)相同

# | closure_parameter_position | no | no | no | style | no | warning 閉包參數(shù)位置, 閉包參數(shù)應(yīng)該 { 左邊在同一行

# | closure_spacing | yes | yes | no | style | no | warning 閉包表達(dá)式在每個(gè)大括號(hào) { } 內(nèi)前后應(yīng)有一個(gè)空格

# | collection_alignment | yes | no | no | style | no | warning, align_colons: false 集合文字中的所有元素應(yīng)垂直對齊

# | conditional_returns_on_newline | yes | no | no | style | no | warning, if_only: false 條件語句與結(jié)果不建議寫在一行 ,例如:guard true else { return } ;if true { return "YES" } else { return "NO" } 會(huì)有 warning提示

# | contains_over_filter_count | yes | no | no | performance | no | warning 推薦使用 contains,避免使用 filter(where:).count 與 0 相比較

# | contains_over_filter_is_empty | yes | no | no | performance | no | warning 推薦使用 contains,避免使用 filter(where:).isEmpty

# | contains_over_first_not_nil | yes | no | no | performance | no | warning 推薦使用 contains,避免使用 first(where:) != nil 與 firstIndex(where:) != nil

# | contains_over_range_nil_comparison | yes | no | no | performance | no | warning 推薦使用 contains,不建議使用與nil的比較

# | convenience_type | yes | no | no | idiomatic | no | warning 用于檢測靜態(tài)成員的類型應(yīng)實(shí)現(xiàn)為無大小寫的枚舉,以避免實(shí)例化

# | custom_rules | no | no | no | style | no | user-defined 通過提供正則表達(dá)式字符串來創(chuàng)建自定義規(guī)則

# | discouraged_object_literal | yes | no | no | idiomatic | no | warning, image_literal: true, color_literal: true 避免使用圖片和顏色的字面量(Ltiteral),盡量使用初始化的方式

# | discouraged_optional_boolean | yes | no | no | idiomatic | no | warning 推薦使用非可選的bool值

# | discouraged_optional_collection | yes | no | no | idiomatic | no | warning 優(yōu)先選擇空集合而不是可選集合

# | empty_collection_literal | yes | no | no | performance | no | warning 優(yōu)先使用isEmpty 空數(shù)組或字典文字進(jìn)行值的比較

# | empty_count | yes | no | no | performance | no | error, only_after_dot: false 建議使用isEmpty判斷,而不是使用count==0判斷

# | empty_string | yes | no | no | performance | no | warning 優(yōu)先使用isEmpty判斷,而不是將字符串與空字符串文字進(jìn)行比較

# | empty_xctest_method | yes | no | no | lint | no | warning 應(yīng)避免使用空的XCTest方法

# | enum_case_associated_values_count | yes | no | no | metrics | no | warning: 5, error: 6 枚舉情況下的關(guān)聯(lián)值數(shù)量應(yīng)少

# | expiring_todo | yes | no | no | lint | no | (approaching_expiry_severity) warning, (reached_or_passed_expiry_severity) error TODO和FIXME應(yīng)該在其到期日之前解決

# | explicit_acl | yes | no | no | idiomatic | no | warning 所有聲明都應(yīng)明確指定訪問控制級(jí)別關(guān)鍵字

# | explicit_enum_raw_value | yes | no | no | idiomatic | no | warning 枚舉應(yīng)設(shè)置默認(rèn)值

# | explicit_init | yes | yes | no | idiomatic | no | warning 避免直接調(diào)用 init 方法

# | explicit_self | yes | yes | no | style | yes | warning 實(shí)例變量和函數(shù)應(yīng)使用“self”顯式訪問

# | explicit_top_level_acl | yes | no | no | idiomatic | no | warning 頂級(jí)聲明應(yīng)明確指定訪問控制級(jí)別關(guān)鍵字

# | explicit_type_interface | yes | no | no | idiomatic | no | warning, excluded: [], allow_redundancy: false 需要跑明確參數(shù)的類型定義

# | extension_access_modifier | yes | no | no | idiomatic | no | warning 優(yōu)先使用擴(kuò)展名訪問修飾符

# | fallthrough | yes | no | no | idiomatic | no | warning 避免在 case語句中使用 fallthrough

# | fatal_error_message | yes | no | no | idiomatic | no | warning fatalError 必須擁有一個(gè) message

# | file_header | yes | no | no | style | no | warning, 標(biāo)頭注釋應(yīng)與項(xiàng)目模式一致 required_string: None, required_pattern: None, forbidden_string: None, forbidden_pattern: None

# | file_length | no | no | no | metrics | no | warning: 文件長度限制 warning:400, error: 1000, ignore_comment_only_lines: false

# | file_name | yes | no | no | idiomatic | no | (severity) warning, 文件名應(yīng)與文件中聲明的類型或擴(kuò)展名匹配(如果有) excluded: ["LinuxMain.swift", "main.swift"], prefixPattern: , suffixPattern: \+.*, nestedTypeSeparator: .

# | file_name_no_space | yes | no | no | idiomatic | no | (severity) warning, excluded: [] 文件名不應(yīng)包含任何空格

# | file_types_order | yes | no | no | style | no | warning, 指定如何排序文件中的類型 order: [[SwiftLintFramework.FileType.supportingType], [SwiftLintFramework.FileType.mainType], [SwiftLintFramework.FileType.extension], [SwiftLintFramework.FileType.previewProvider]]

# | first_where | yes | no | no | performance | no | warning 使用 `.first(where:)` 代替 `.filter { }.first`

# | flatmap_over_map_reduce | yes | no | no | performance | no | warning 推薦使用 flatMap,避免使用 map 的 reduce([], +)

# | for_where | no | no | no | idiomatic | no | warning 使用 `for where` 代替 簡單的 `for { if }`

# | force_unwrapping | yes | no | no | idiomatic | no | warning 避免強(qiáng)制解包

# | function_default_parameter_at_end | yes | no | no | idiomatic | no | warning 方法中參數(shù)列表,應(yīng)將帶有默認(rèn)值的參數(shù)放在最后面

# | function_parameter_count | no | no | no | metrics | no | warning: 5, error: 8 ignores_default_parameters: true 函數(shù)參數(shù)個(gè)數(shù) 默認(rèn) 5 warning 8 error

# | ibinspectable_in_extension | yes | no | no | lint | no | warning 擴(kuò)展不應(yīng)添加@IBInspectable屬性

# | identical_operands | yes | no | no | lint | no | warning 比較兩個(gè)相同的操作數(shù)可能是一個(gè)錯(cuò)誤

# | identifier_name | no | no | no | style | no | 參數(shù)變量命名規(guī)則 (min_length) w/e: 3/2, (max_length) w/e: 40/60, excluded: [], allowed_symbols: [], validates_start_with_lowercase: true

# | implicit_return | yes | yes | no | style | no | warning, included: [closure, function, getter] 在閉包,函數(shù)和getter中更喜歡隱式返回

# | implicitly_unwrapped_optional | yes | no | no | idiomatic | no | warning, mode: allExceptIBOutlets 避免隱式解析可選類型的使用 / 避免隱式解包(定義 ! 類型)

# | indentation_width | yes | no | no | style | no | severity: warning, indentation_width: 4 include_comments: true 縮進(jìn)長度

# | joined_default_parameter | yes | yes | no | idiomatic | no | warning 不推薦顯式使用默認(rèn)分隔符

# | large_tuple | no | no | no | metrics | no | warning: 2, error: 3 元祖成員 元組沖突:元組應(yīng)該最多有2個(gè)成員,多余兩個(gè)會(huì)報(bào)錯(cuò)

# | last_where | yes | no | no | performance | no | warning 推薦在集合中使用:.last(where:) 不推薦使用: .filter { }.last

# | leading_whitespace | no | yes | no | style | no | warning 文件末尾不應(yīng)該存在空格符

# | legacy_multiple | yes | no | no | idiomatic | no | warning 推薦使用isMultiple(of:)函數(shù),不推薦使用余數(shù)運(yùn)算符(%)

# | legacy_random | yes | no | no | idiomatic | no | warning 隨機(jī)函數(shù) 優(yōu)先使用type.random(in :),不建議使用舊版函數(shù)。

# | let_var_whitespace | yes | no | no | style | no | warning Let和var應(yīng)該用空白行與其他語句分開

# | line_length | no | no | no | metrics | no | warning: 120, error: 200, 行的字符長度,這個(gè)強(qiáng)烈不推薦使用。官方的規(guī)定是超過120字符就給warning, ignores urls: false, ignores function declarations: false, ignores comments: false, ignores interpolated strings: false

# | literal_expression_end_indentation | yes | yes | no | style | no | warning 數(shù)組和字典文字的結(jié)尾應(yīng)與開始它的行具有相同的縮進(jìn)

# | lower_acl_than_parent | yes | no | no | lint | no | warning 確保定義的訪問控制級(jí)別低于其父級(jí)

# | mark | no | yes | no | lint | no | warning 正確使用 mark 的格式 `// MARK: - message`

# | missing_docs | yes | no | no | lint | no | warning: open, public 聲明應(yīng)記錄在案

# | modifier_order | yes | yes | no | style | no | warning, 修飾符順序應(yīng)一致 preferred_modifier_order: [override, acl, setterACL, dynamic, mutators, lazy, final, required, convenience, typeMethods, owned]

# | multiline_arguments | yes | no | no | style | no | warning, 參數(shù)應(yīng)該在同一行,或者每行一個(gè) first_argument_location: any_line, only_enforce_after_first_closure_on_first_line: false

# | multiline_arguments_brackets | yes | no | no | style | no | warning 多行參數(shù)應(yīng)在其新行中包含方括號(hào) []

# | multiline_function_chains | yes | no | no | style | no | warning 鏈接的函數(shù)調(diào)用應(yīng)該在同一行上,或者每行一個(gè)

# | multiline_literal_brackets | yes | no | no | style | no | warning 多行文字應(yīng)在其新行中包含方括號(hào) []

# | multiline_parameters | yes | no | no | style | no | warning 函數(shù)和方法參數(shù)應(yīng)該在同一行上,或者每行一個(gè)

# | multiline_parameters_brackets | yes | no | no | style | no | warning 多行參數(shù)應(yīng)在其新行中包含方括號(hào)

# | multiple_closures_with_trailing_closure | no | no | no | style | no | warning 傳遞多個(gè)閉包參數(shù)時(shí),不應(yīng)使用結(jié)尾的閉包語法

# | nimble_operator | yes | yes | no | idiomatic | no | warning 避免 expect 一個(gè)確定的判斷

# | no_extension_access_modifier | yes | no | no | idiomatic | no | error 禁止使用擴(kuò)展訪問修飾符

# | no_grouping_extension | yes | no | no | idiomatic | no | warning 擴(kuò)展名不應(yīng)用于對同一源文件中的代碼進(jìn)行分組

# | notification_center_detachment | no | no | no | lint | no | warning `NotificationCenter.default.removeObserver` 只在 `deinit` 中被調(diào)用

# | nslocalizedstring_key | yes | no | no | lint | no | warning 應(yīng)將靜態(tài)字符串用作NSLocalizedString中的鍵。

# | nslocalizedstring_require_bundle | yes | no | no | lint | no | warning 調(diào)用NSLocalizedString應(yīng)該指定包含字符串文件的捆綁軟件

# | number_separator | yes | yes | no | style | no | warning, minimum_length: 0 下劃線數(shù)字分隔符

# | object_literal | yes | no | no | idiomatic | no | warning, image_literal: true, color_literal: true 避免 image and color 使用字面量初始化, 需要把相關(guān)圖片名,顏色RGB 等參數(shù)定義為 enum struct 或者常量

# | operator_usage_whitespace | yes | yes | no | style | no | warning 操作符需要使用一個(gè)空格間隔

# | operator_whitespace | no | no | no | style | no | warning 當(dāng)定義空格操作符的時(shí)候,被定義的名字或類型兩邊應(yīng)該各有一個(gè)單行空格操作符

# | optional_enum_case_matching | yes | yes | no | style | no | warning 將枚舉大小寫與不帶'?'的可選枚舉匹配 在Swift 5.1及更高版本中受支持

# | orphaned_doc_comment | no | no | no | lint | no | warning 注釋要寫在聲明中

# | overridden_super_call | yes | no | no | lint | no | warning, excluded: [[]], included: [["*"]] override 方法需要調(diào)用 super method

# | override_in_extension | yes | no | no | lint | no | warning 擴(kuò)展不應(yīng)覆蓋聲明

# | pattern_matching_keywords | yes | no | no | idiomatic | no | warning 通過將關(guān)鍵字移出元組來組合多個(gè)模式匹配綁定

# | prefer_self_type_over_type_of_self | yes | yes | no | style | no | warning 訪問屬性或調(diào)用方法時(shí),最好將“自類型”設(shè)置為(of:self)。

# | prefer_zero_over_explicit_init | yes | yes | no | idiomatic | no | warning 優(yōu)先使用.zero而不是具有零參數(shù)的顯式init(例如CGPoint(x:0,y:0))

# | prefixed_toplevel_constant | yes | no | no | style | no | warning, only_private: false 頂級(jí)常量的前綴應(yīng)為k

# | private_action | yes | no | no | lint | no | warning IBActions應(yīng)該是私有的

# | private_outlet | yes | no | no | lint | no | warning, IBOutlets 應(yīng)該設(shè)置為 private, 來避免泄露 allow_private_set: false

# | prohibited_interface_builder | yes | no | no | lint | no | warning 禁止用interface Builder 創(chuàng)建視圖

# | prohibited_super_call | yes | no | no | lint | no | warning, 某些特殊的 override 方法, 禁止調(diào)用 super method excluded: [[]], included: [["*"]]

# | quick_discouraged_call | yes | no | no | lint | no | warning 不鼓勵(lì)在“describe”和/或“context” 框中進(jìn)行調(diào)用。

# | quick_discouraged_focused_test | yes | no | no | lint | no | warning 不鼓勵(lì)重點(diǎn)測試。專注于此測試時(shí),其他測試將不會(huì)運(yùn)行

# | quick_discouraged_pending_test | yes | no | no | lint | no | warning 不推薦:未開始的測試。標(biāo)記為待定時(shí),該測試不會(huì)運(yùn)行

# | raw_value_for_camel_cased_codable_enum | yes | no | no | lint | no | warning 設(shè)置枚舉建議設(shè)置默認(rèn)值

# | reduce_into | yes | no | no | performance | no | warning 對于 copy-on-write 類型,推薦使用 reduce(into:_:) 不建議使用 reduce(_:_:)

# | redundant_nil_coalescing | yes | yes | no | idiomatic | no | warning #避免使用 `object ?? nil` 僅當(dāng)lhs為nil時(shí)才評估nil合并運(yùn)算符,而n為rhs則合并nil合并運(yùn)算符

# | redundant_optional_initialization | no | yes | no | idiomatic | no | warning 用nil初始化可選變量是多余的。 # 默認(rèn)值賦值為nil 不需要寫默認(rèn)值為 nil

# | redundant_string_enum_value | no | no | no | idiomatic | no | warning #在定義字符串枚舉的時(shí)候, 當(dāng)字符串枚舉值等于枚舉名稱時(shí),可以不用賦值

# | redundant_type_annotation | yes | yes | no | idiomatic | no | warning 變量不應(yīng)具有冗余類型注釋 建議 var url = URL() 不建議 var url : URL = URL()

# | redundant_void_return | no | yes | no | idiomatic | no | warning 在函數(shù)聲明中返回Void是多余的。#在不必要的時(shí)候, 不需要寫 ->() and -> Void

# | required_deinit | yes | no | no | lint | no | warning 類應(yīng)具有顯式的deinit方法。

# | required_enum_case | yes | no | no | lint | no | No protocols configured. 符合指定協(xié)議的枚舉必須實(shí)現(xiàn)特定情況。 In config add 'required_enum_case' to 'opt_in_rules' and config using :\n\n'required_enum_case:\n {Protocol Name}:\n {Case Name}:{warning|error}\n {Case Name}:{warning|error}\n

# | return_arrow_whitespace | no | yes | no | style | no | warning # -> 前后要有空格,函數(shù)定義返回的 -> 前后有空格, 不換行

# | shorthand_operator | no | no | no | style | no | error # 使用+= , -=, *=, /=? 代替 a = a + 1

# | single_test_class | yes | no | no | style | no | warning 測試文件應(yīng)只包含一個(gè)QuickSpec或XCTestCase類。

# | sorted_first_last | yes | no | no | performance | no | warning 優(yōu)先使用min()或max() 不建議使用 sorted().first或sorted().last

# | sorted_imports | yes | yes | no | style | no | warning Imports 應(yīng)排序.

# | statement_position | no | yes | no | style | no | (statement_mode) default, (severity) warning #這里主要指的是 else 和 catch 前面要加一個(gè)空格, 也不能大于1個(gè)空格

# | static_operator | yes | no | no | idiomatic | no | warning 應(yīng)該將運(yùn)算符聲明為靜態(tài)函數(shù),而不是自由函數(shù)。

# | strict_fileprivate | yes | no | no | idiomatic | no | warning fileprivate 應(yīng)該避免。

# | strong_iboutlet | yes | no | no | lint | no | warning @IBOutlets不應(yīng)被聲明為weak 應(yīng)該為 strong。

# | switch_case_on_newline | yes | no | no | style | no | warning #switch 的 case 需要新啟一行

# | syntactic_sugar | no | yes | no | idiomatic | no | warning #語法糖[Int] 代替Array / 例:要使用 [] ? 等數(shù)組字典可選項(xiàng)的語法糖

# | toggle_bool | yes | yes | no | idiomatic | no | warning 不讓使用 A = !A 建議使用 A.toggle()

# | trailing_closure | yes | no | no | style | no | warning, only_single_muted_parameter: false 盡可能使用尾隨閉包語法。

# | trailing_newline | no | yes | no | style | no | warning #末尾空行,文件末尾應(yīng)該有一個(gè)空行

# | type_body_length | no | no | no | metrics | no | warning: 200, error: 350 #類型體長度。類型體長度不應(yīng)該跨越太多行,超過200行給warning,超過350行給error,可自定義enum或struct

# | type_contents_order | yes | no | no | style | no | warning, 指定類型內(nèi)子類型,屬性,方法及更多內(nèi)容的順序。 order: [[SwiftLintFramework.TypeContent.case], [SwiftLintFramework.TypeContent.typeAlias, SwiftLintFramework.TypeContent.associatedType], [SwiftLintFramework.TypeContent.subtype], [SwiftLintFramework.TypeContent.typeProperty], [SwiftLintFramework.TypeContent.instanceProperty], [SwiftLintFramework.TypeContent.ibInspectable], [SwiftLintFramework.TypeContent.ibOutlet], [SwiftLintFramework.TypeContent.initializer],... |

# | unavailable_function | yes | no | no | idiomatic | no | warning 未實(shí)現(xiàn)的功能應(yīng)標(biāo)記為不可用。

# | unneeded_break_in_switch | no | no | no | idiomatic | no | warning # 在switch-case語句中, 有方法調(diào)用或操作時(shí),避免使用break語句

# | unneeded_parentheses_in_closure_argument | yes | yes | no | style | no | warning 聲明閉包參數(shù)時(shí),不需要括號(hào)。

# | unowned_variable_capture | yes | no | no | lint | no | warning 最好將引用捕獲為弱引用以避免潛在的崩潰。

# | untyped_error_in_catch | yes | yes | no | idiomatic | no | warning 沒有類型轉(zhuǎn)換,catch語句不應(yīng)聲明錯(cuò)誤變量。

# | unused_capture_list | no | no | no | lint | no | warning #閉包中沒有被使用的參數(shù)應(yīng)該刪除

# | unused_closure_parameter | no | yes | no | lint | no | warning #函數(shù)的參數(shù)必須被使用

# | unused_declaration | yes | no | no | lint | yes | severity: error, include_public_and_open: false 在所有被刪除的文件中,聲明至少應(yīng)被引用一次。

# | unused_optional_binding | no | no | no | style | no | warning, ignore_optional_try: false #在使用if判斷某變量是否為nil的時(shí)候, 不建議使用下劃線(_) 必須使用定義的 optional binding

# | vertical_parameter_alignment | no | no | no | style | no | warning #函數(shù)參數(shù)分為多行書寫的時(shí)候, 頭部(小括號(hào)后面一位)必須對齊 函數(shù)參數(shù)分為多行書寫的時(shí)候, 頭部(小括號(hào)后面一位)必須對其

# | vertical_parameter_alignment_on_call | yes | no | no | style | no | warning 如果函數(shù)參數(shù)在方法調(diào)用中位于多行中,則應(yīng)垂直對齊。

# | vertical_whitespace | no | yes | no | style | no | warning, max_empty_lines: 1 #垂直方向上的空格行,限制為一行(注釋除外) 不能有連續(xù)多個(gè)空行

# | vertical_whitespace_between_cases | yes | yes | no | style | no | warning 在 switch cases 之間包括一條空行。

# | vertical_whitespace_closing_braces | yes | yes | no | style | no | N/A 在關(guān)閉大括號(hào)之前,請勿包括垂直空格(空行)。

# | vertical_whitespace_opening_braces | yes | yes | no | style | no | N/A 打開花括號(hào)后,請勿包括垂直空格(空行)。

# | weak_delegate | no | yes | no | lint | no | warning 代表應(yīng)有弱勢,以避免參考周期。

# | xct_specific_matcher | yes | no | no | idiomatic | no | warning 優(yōu)先使用特定的XCTest匹配器,XCTAssertEqual而不是XCTAssertNotEqual

# | yoda_condition | yes | no | no | lint | no | warning 變量應(yīng)位于比較運(yùn)算符的左側(cè),常數(shù)應(yīng)位于右側(cè)。

可去官方自定查找對應(yīng)屬性說明:

?(GitHub 鏈接:https://github.com/realm/SwiftLint)

3.4.swiftlint.yml配置文件的嵌套

在我們使用.swift.yml配置文件的時(shí)候,如果在系統(tǒng)掃描的過程中發(fā)現(xiàn)了一個(gè)新的配置文件,那么子目錄下的規(guī)則就會(huì)改為新的配置規(guī)則。

??總的來說,swiftlint對于代碼規(guī)范管理是利遠(yuǎn)大于弊的,只要在前期需要一些時(shí)間建立一個(gè)規(guī)范體系.在后期由于代碼整體的規(guī)范性,能利于項(xiàng)目的維護(hù),以及可以節(jié)省代碼Review的時(shí)間.

參考:

SwiftLint GitHub地址:https://github.com/realm/SwiftLint

SwiftLint 官網(wǎng)說明.:https://realm.github.io/SwiftLint/index.html

AST:http://clang.llvm.org/docs/IntroductionToTheClangAST.html

LLVM優(yōu)點(diǎn):https://www.cnblogs.com/zuopeng/p/4141467.html

swift代碼規(guī)范工具:http://www.itdecent.cn/p/eea2520f34ae

iOS- 工程配置SwiftLint:https://www.it610.com/article/1282270515729285120.htm

SwiftLint 規(guī)則:https://cloud.tencent.com/developer/article/1617958



作者:技術(shù)進(jìn)階在路上

鏈接:http://www.itdecent.cn/p/abb4057db849

來源:簡書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容