目的:
? 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í)行效率。如果對編譯過程理解不太清楚,可以參考:AST和LLVM優(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)載請注明出處。