Swift語法手記

1、Swift 調 OC

在橋接文件 SwiftDemo-Bridging-Header.h 里導入,如:

#import <MJRefresh/MJRefresh.h>
#import "TestView.h"
#import "UIView+HUD.h"

但需要注意的是,OC 的方法經過系統自動轉換為 Swift 代碼后,方法名可能會發生變化,比如單例方法:

+ (instancetype)sharedModel;

在 Swift 里的調用為:

UserModel.shared()

2、OC 調 Swift

導入 項目名-Swift.h,這是一個隱式的文件,這里面有系統自動轉換的 Swift 代碼。

#import "SwiftDemo-Swift.h"

注意要標注了 @objc 的屬性和方法,才會被系統自動轉換成 OC 的代碼。比如:

Swift 屬性:

@objc var urlStr: String?

系統自動轉換成的 OC 屬性:

@property (nonatomic, copy) NSString * _Nullable urlStr;

Swift 方法:

@objc public func cancelAllRequest() {}

系統自動轉換成的 OC 方法:

- (void)cancelAllRequest;

3、Swift中的宏

Swift 里沒有宏的這個概念,可以使用全局常量、全局變量、全局方法來代替。比如:

OC 的宏

#define kAppWidth          UIScreen.mainScreen.bounds.size.width

Swift 的全局常量

let kAppWidth = UIScreen.main.bounds.size.width

OC 的宏

#define kImageNamed(NAME)  [UIImage imageNamed:NAME]

Swift 的全局方法

func kImageNamed(_ name: String) -> UIImage? {
    return UIImage.imageNamed(name)
}

4、分類的處理

Swift 的分類的應用比 OC 多,在一個 Swift 的類里,經常使用一個分類來實現某個功能模塊,比如:

// MARK: - TableView
extension SwiftController: UITableViewDelegate, UITableViewDataSource {}

// MARK: - 點擊事件
extension SwiftController {}

給系統類添加方法,比如:

extension Dictionary {
    // MARK: 字典轉字符串
    func stringValue() -> String? {
        let data = try? JSONSerialization.data(withJSONObject: self, options: [])
        let str = String(data: data!, encoding: String.Encoding.utf8)
        return str
    }
}

OC 的分類,在橋接文件 SwiftDemo-Bridging-Header.h 里導入后,可以直接調用,比如:

導入頭文件

#import "UIImage+Extention.h"


OC 分類的方法聲明

@interface UIImage (Extention)

/// 水印圖片
- (UIImage *)waterImage;

@end


Swift 調用方法

let waterImg: UIImage = image!.water()

5、一些需要特別注意的語法

語法文檔:https://swiftgg.gitbook.io/swift

5.1、類型轉換

Swift 中,值永遠不會被隱式轉換為其他類型,只能顯式轉換,比如:

let a = 10;
let b = 1.0;
let c = a + Int(b);
let d = Double(a) + b;
let f = String(a)
let g = "\(a)"

5.2、數組字典初始化

let arr0: [Int] = []
let arr1: [Int] = [Int]()
let arr2: [Int] = [Int].init()
let arr3: [Int] = [Int].init(repeating: 0, count: 5)

let dict0: [String: Int] = [:]
let dict1: [String: Int] = [String: Int]()
let dict2: [String: Int] = [String: Int].init()

// 閉包式初始化, 只會執行一次
let arr4: [Int] = { () -> [Int] in
    return [1, 2]
}()

// 閉包式初始化, 可省略 () -> [Int] in
let arr5: [Int] = {
    return [3, 4]
}()

5.3、循環/遍歷

forin 循環:

let interestingNumbers = [
    "Prime": [2, 3, 5, 7, 11, 13]
]
for (key, numbers) in interestingNumbers {
    for number in numbers {
    }
    
    for (index, value) in numbers.enumerated() {
    }
}

while 循環:

var n = 2
while n < 100 {
    n *= 2
}

var m = 2
repeat {
    m *= 2
} while m < 100

區間循環:..< 創建的范圍不包含上界,如果想包含的話使用 ...

for i in 0..<4 {}

for i in 0...4 {}

let names: [String] = ["a", "b", "c", "d"]
for name in names[2...] {
    print(name)
}

for name in names[...2] {
    print(name)
}

5.4、解包

if 加 感嘆號(!)強制解包:

if convertedNumber != nil {
    print("convertedNumber has an integer value of \(convertedNumber!).")
}

if let 解包:

if let constantName = someOptional {
    // someOptional 有值
} else {
    // someOptional 為空
}

if let num = Int(optionalNum), let constantName = optionalName, num > 10  {
    // optionalNum 有值,optionalName 也有值,且 num 大于10
}

guard let 解包:

guard let name = person["name"] else {
     // person["name"] 為空會走到這里
    return
}
// person["name"] 有值會繼續往下執行

5.5、字符串

String 與 NSString 的無縫橋接:

var str: String = "a"
let nsStr: NSString = str as NSString
str = nsStr as String

多行字符串 ("""):

let quotation = """
The White Rabbit put on his spectacles.  "Where shall I begin,
please your Majesty?" he asked.

"Begin at the beginning," the King said gravely, "and go on
till you come to the end; then stop."
"""

遍歷:

for character in "Dog!??" {
    print(character)
}
    
for character in [Character]("Dog!??") {
    print(character)
}
    
for character in Array("Dog!??") {
    print(character)
}

字符轉字符串:

let catCharacters: [Character] = ["C", "a", "t", "!", "??"]
let catString = String(catCharacters)
print(catString)
// 打印輸出:“Cat!??”

String 的獲取索引、插入、刪除等操作比較繁瑣,常轉為 NSString 然后去處理:

let str = "Guten Tag!"
str[str.startIndex]     // G
str[str.index(before: str.endIndex)]        // !
str[str.index(after: str.startIndex)]   // u
let index = str.index(str.startIndex, offsetBy: 7)
str[index]      // a


var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)

let range = welcome.index(welcome.endIndex, offsetBy: -2)..<welcome.endIndex
welcome.removeSubrange(range)

5.6、值類型和引用類型

Swift 中結構體和枚舉是值類型,類(class)是引用類型。

Swift 中所有的基本類型:整數(Int)、浮點數(Float/Double)、布爾值(Bool)、字符串(String)、數組(Array)和字典(Dictionary),都是值類型,其底層也是使用結構體實現的。

值類型在被賦值給一個變量、常量或者被傳遞給一個函數的時候,傳過去的是拷貝后的值。

let str0: String = "a"
var str1 = str0
str1 += "b"
print("str0 = \(str0), str1 = \(str1)")
// str0 = a, str1 = ab
引用類型在被賦予到一個變量、常量或者被傳遞到一個函數時,傳過去的是內存地址。

let nsStr0: NSMutableString = NSMutableString.init(string: "a")
let nsStr1 = nsStr0
nsStr1.append("b")
print("nsStr0 = \(nsStr0), nsStr1 = \(nsStr1)")
// nsStr0 = ab, nsStr1 = ab

5.7、set/get

重寫 set/get:

var num: Int {
    get {
        return 0
    }
    set(newNum) {
        print("\(newNum)")
    }
}

簡化 Setter 聲明,計算屬性的 setter 沒有定義表示新值的參數名的時候,可以使用默認名稱 newValue:

var num: Int {
    get {
        return 0
    }
    set {
        print("\(newValue)")
    }
}

簡化 Getter 聲明, 在 getter 中忽略 return:

var num: Int {
    get {
        0
    }
    set {
        print("\(newValue)")
    }
}

只讀計算屬性,只有 getter 沒有 setter 的計算屬性叫只讀計算屬性:

// 只讀屬性 get 的簡略寫法, 每次都會執行里面的代碼
var kTopWindow: UIWindow {
    var window = UIApplication.shared.keyWindow!
    if #available(iOS 13, *) {
        for wScene in UIApplication.shared.connectedScenes where wScene.activationState != UIScene.ActivationState.unattached {
            if let windowScene = wScene as? UIWindowScene, windowScene.windows.count > 0 {
                window = windowScene.windows.last!
                break
            }
        }
    }
    return window
}

5.8、類型轉換

檢查類型, 用類型檢查操作符(is)來檢查一個實例是否屬于特定子類型:

let num = 10
if num is Int {
    print(num)
}

類型轉換操作符(as? 或 as!):

let num = 10
if let newNum = num as? Int {
    print(newNum)
} else {}
    
let newNum = num as! Int

5.9、弱引用(weak)

weak var weakSelf = self
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
    guard let strongSelf = weakSelf else {
        return
    }
}

5.10、訪問控制

open 和 public 級別可以讓實體被同一模塊源文件中的所有實體訪問,在模塊外也可以通過導入該模塊來訪問源文件里的所有實體。
通常情況下,可以使用 open 或 public 級別來指定框架的外部接口。
open 只能作用于類和類的成員,它和 public 的區別主要在于 open 限定的類和成員能夠在模塊外能被繼承和重寫。

internal 級別讓實體被同一模塊源文件中的任何實體訪問,但是不能被模塊外的實體訪問。
通常情況下,如果某個接口只在應用程序或框架內部使用,就可以將其設置為 internal 級別, 也是系統默認的訪問級別。

fileprivate 限制實體只能在其定義的文件內部訪問。
如果功能的部分實現細節只需要在文件內使用時,可以使用 fileprivate 來將其隱藏。

private 限制實體只能在其定義的作用域,以及同一文件內的 extension 訪問。
如果功能的部分細節只需要在當前作用域內使用時,可以使用 private 來將其隱藏。

5.11、單例

static let sharedInstance: NetWorkSwift = NetWorkSwift()

5.12、GCD

DispatchQueue.main.async {}

DispatchQueue.global().async {}

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {}

5.12、閉包

閉包表達式語法:

{ (parameters) -> return type in
    statements
}

普通閉包, 順序執行,不能延時:

private func p_normal(finish: (_ num: Int) -> Void) {
    finish(10)
}

逃逸閉包, 可以延時:當一個閉包作為參數傳到一個函數中,但是這個閉包在函數返回之后才被執行,我們稱該閉包從函數中逃逸。

private func p_escaping(finish: @escaping (_ num: Int) -> Void) {
    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
        finish(20)
    }
    print(30)
}

調用
p_escaping { num in
    print(num)
}
// 先打印 30,再打印 20

5.13、系統版本判斷

if #available(iOS 13, *) {}

5.14、面向協議編程

普通協議:

//定義協議
protocol Shakeable {
    func shake()
}

//實現協議
class ShakeView: UIView, Shakeable {
    func shake() {
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.25
        animation.repeatCount = 5
        animation.autoreverses = true
        animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
        animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
        self.layer.add(animation, forKey: "position")
    }
}

//實現協議
class AnotherShakeView: UIView, Shakeable {
    func shake() {
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.05
        animation.repeatCount = 5
        animation.autoreverses = true
        animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
        animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
        self.layer.add(animation, forKey: "position")
    }
}

面向協議:

//定義協議
protocol Shakeable {}

//實現協議
extension Shakeable where Self : UIView {
    func shake() {
        let animation = CABasicAnimation(keyPath: "position")
        animation.duration = 0.25
        animation.repeatCount = 5
        animation.autoreverses = true
        animation.fromValue = CGPoint.init(x: self.center.x - 4.0, y: self.center.y)
        animation.toValue = CGPoint.init(x: self.center.x + 4.0, y: self.center.y)
        layer.add(animation, forKey: "position")
    }
}

class ShakeView: UIView, Shakeable {}
class AnotherShakeView: UIView, Shakeable {}

6、代碼規范檢測工具 SwiftLint

https://github.com/realm/SwiftLint

https://www.bbsmax.com/A/xl56GAykdr/

使用 cocoapods 引入:

pod 'SwiftLint'

Xcode 設置,在 Build Phases 中新增一個 Run Script:

"${PODS_ROOT}/SwiftLint/swiftlint"

配置自定義規則:

用命令行創建配置文件:
touch .swiftlint.yml

用命令行顯示隱藏文件:
defaults write com.apple.finder AppleShowAllFiles -bool true

找到 .swiftlint.yml,開始設置規則:
規則參考:https://github.com/realm/SwiftLint

注意:使用的時候,將.swiftlint.yml 放在需要執行swiftlint工程的根目錄中,整個工程會執行.swiftlint.yml的配置;
     如果在二級目錄同樣配置了.swiftlint.yml文件,則會執行二級目錄下的配置

7、常見第三方

網絡類使用 Alamofire、moya

布局 snapkit

json處理引入 SwiftJson、HandyJSON

圖片庫 Kingfisher

響應式編程 RxSwift

加解密 CryptoSwift

數據庫 SQLite.swift、WCDB.swift

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容