Swift語言基礎.jpg
簡介
- Swift 語言由蘋果公司在2014年推出,用來撰寫 macOS 和 iOS 應用程序
Swift 語言學習路線及重難點
- 常量與變量
- 數據類型
- 運算符
- 元組
- 邏輯分支
- 循環
- 字符串
- 數組
- 字典
- 可選型 (重點)
- 類型轉換(轉化符號is和as)
- 函數
- 閉包
- 枚舉
- 結構體
- 類
- 屬性與方法
- 構造與析構函數
- 協議 protocol
- 擴展 extension
- 泛型
- 異常 和 Result
- 元類型、.self 與 Self
- @objc關鍵字
- where關鍵字
- Key Path
- Codable協議
- 訪問權限
- 混合開發
- 命名空間
- 學習參考
常量與變量
什么是常量和變量
- 常量:使用
let
定義,定義后不可修改 - 變量:使用
var
定義,定義后可以修改
常量和變量的使用注意
- 在開發中,建議先定義常量,如需修改再修改為變量(更加安全)
- 聲明為常量不可修改的意思是 指針不可以再指向其他對象,但是可以通過指針拿到對象,修改其中屬性
數據類型
- Swift中的數據類型有:整型/浮點型/Bool型/元組/枚舉/結構體/對象類型等
類型推導
- Swift是強類型語言,是一種總是強制類型定義的語言,要求所有變量都必須先定義后使用
- 注意:
- 定義一個標識符時有直接進行賦值,標識符后面的類型可以省略
- Swift有類型推導,會自動根據后面的賦值來決定前面的標識符的數據類型
運算符
常見的運算符
- +、-、*、/、%
- =、+=、-=、*=、/=、%=
- >、>=、<、<=、==、!=
- 區間運算符
- 半開半閉區間:0..<10 表示0~9
- 閉區間:0...10 表示0~10
- &&、||、!
元組
- 元組:一種數據結構,可以用于定義一組數據,組成元祖的數據可以稱為“元素”
// 元組的常見寫法
var one = ("李四", 30, 1.75)
var two = (name:"李四", age:30, height:1.75)
let (errorCode, errorInfo) = (404, "Not Found")
邏輯分支
// if 的使用
if a > 9 {
print(a)
}
// guard 的使用
guard 條件表達式 else {
// guard是Swift2.0新增的語法,跳轉語句一般是return、break、continue、throw
}
語句組
switch 分支
- 一個case判斷,可以判斷多個值,以 , 隔開
- 如需case穿透,使用關鍵字
fallthrough
- 支持區間判斷和多種數據類型、浮點型、字符串類型等
循環
for 循環
// for in 循環
for i in 0..<10 {
print(i)
}
// 特殊寫法 如不需要用下標i
for _ in 0...10 {
print("hello swift")
}
while 和 repeat while 循環
var a = 0
while a < 10 {
a = a + 1
}
var b = 0
repeat {
b = b + 1
} while b < 20
字符串
- String是一個結構體,NSString是OC對象,String性能更高
- String支持直接遍歷
字符串常用操作
// 1、拼接 使用 + 或 append
let str = "abc" + "def"
// 2、遍歷
for (index, value) in str.enumerated() {
print("\(index) --- \(value)")
}
// 3、大寫或小寫
str.lowercased().uppercased()
// 4、含有字符串
str.contains("cd")
// 5、分割
let str1 = "aa&$$bb$$cc$$dd"
let desc = str1.components(separatedBy: "$$")
// 6、替換
let desc1 = str1.replacingOccurrences(of:"$$", with:"**")
// 7、子串
str.prefix(5) // 截取前5個字符
str.suffix(5) // 截取后5個字符
str.index(str.startIndex, offsetBy: -2)
let sub1 = str[0..<5] // 從位置0開始到5結束獲取字符串
數組
- 數組是一堆有序的由相同類型元素構成的集合
- 數組中的元素是有序的,可重復出現
- Swift中用Array表示數組,是一個結構體,可以放普通類型
// 定義
var array = ["zhangsan", "lisi", "wangwu"]
// 基本操作
array.count // 獲取長度
array.isEmpty // 判空
array.append("l") // 添加數據
array.insert("wo", at:0) // 插入元素
array.dropFirst() // 刪除元素
array[0] = "fangqi" // 修改元素
array.reverse() // 倒序
// 遍歷
for (index, name) in array.enumerated() {
print(index)
print(name)
}
字典
// Swift中任意類型用Any表示,如下定義字典
var dict: [String:Any] = ["name":"張三", "age":18]
// 基本操作
dict.count // 獲取長度
dict.isEmpty // 判空
dict["height"] = 1.82 // 添加數據
dict.removeValue(forKey: "height") // 刪除字段
dict["name"] = "lisi" // 修改字典 或使用 dict.updateValue("lisi", forKey:"name")
// 遍歷
for (key, value) in dict {
print("\(key) --- \(value)")
}
可選型 (重點)
- 可選類型(Optional)的取值為:有值 | nil
// 定義可選類型
let name: String? = nil
// 取出可選類型的值 ! 強制解包(顯示解包)
print(name!) // 如果可選類型為nil,會報錯
// 可選綁定(隱式解包)
if let str = name {
print(str) // 此時輸出就是str的值,而不是Optional
}
// 或使用guard取出可選類型的值
guard let str = name else {
return
}
print(str)
類型轉換
類型轉化符號 is 和 as
// 定義數組
let array: [Any] = [12, "zhangsan"]
// 取出數組中最后一個元素
let objcLast = array.last!
// is 判斷元素是否是一個Int類型
if objcLast is Int {
print("是Int類型")
}
// as? 將Any轉成可選類型,通過判斷可選類型是否有值,來決定是否轉化成功了
let name = objcLast as? String
print(name) // 結果:Optional("zhangsan")
// as! 將Any轉成具體的類型,如果不是該類型,那么程序會崩潰
let name2 = objcLast as! String
print(name2) // 結果:zhangsan
Any、AnyObject
- Any是一個空協議集合的別名,它表示沒有實現任何協議,因此它可以是任何類型,包括類實例與結構體實例??梢员硎救魏晤愋?,包括函數類型
- AnyObject是一個成員為空的協議,任何對象都實現了這個協議。可以表示任何類類型的實例
函數
func 函數名(參數列表) -> 返回值類型 {
return 返回值
}
函數的使用注意
- 函數參數沒有用var和let修飾,但它是常量,不能在函數內修改
- 每個函數的形式參數都包含形式參數標簽和形式參數名兩部分
- 某些情況,如果沒傳入具體的參數,可以使用默認參數
- 可變參數,可接受不確定數量的參數,必須有相同的類型
- 默認函數參數是值傳遞,如想改變外面變量,使用
inout
關鍵字 - 函數的嵌套,不推薦該寫法
函數類型
- 函數是 引用類型
- 每個函數都有屬于自己的類型,由函數的 參數類型 和 返回類型 組成
- 有了函數類型,就可以把函數類型像Int、Double、Array來用,比如函數類型 (Int, Int) -> Int
閉包
// 閉包表達式
{ (parameters) -> (return type) in
statements
}
- 閉包表達式由一對 {} 開始與結束
- 由 in 關鍵字將閉包分割成兩部分:參數與返回值、閉包體
- 閉包形參不能提供默認值
閉包參數名稱縮寫
let array = getList(score: [65,75,85,95], op: { (num: Int) -> Bool in return num>80 })
// 簡寫一:省略 -> 與返回值類型
let array1 = getList(score: [65,75,85,95], op: { (num: Int) in return num>80 })
// 簡寫二:省略參數類型和括號
let array2 = getList(score: [65,75,85,95], op: { num in return num>80 })
// 簡寫三:省略 return 關鍵字
let array3 = getList(score: [65,75,85,95], op: { num in num>80 })
// 簡寫四:參數名稱縮寫,省略參數聲明和 in,改為$0
let array4 = getList(score: [65,75,85,95], op: { $0>80 })
捕獲
- 閉包可以在上下文環境中捕獲常量、變量、并在自己的作用域內使用
- Swift最簡單的閉包形式是嵌套函數,可以捕獲其外部函數所有的參數以及定義的常量和變量
尾隨閉包
- 是一個寫在函數括號之后的閉包表達式,函數支持將其做為最后一個參數調用
func doSomething(info: String, clousre: (String) -> Void) {
clousre(info)
}
// 使用尾隨閉包進行函數調用
doSomething(info: "World") { s in
print(s)
}
逃逸閉包
- 閉包作為一個參數傳遞給一個函數
- 傳入函數的閉包如果在函數執行結束后才會調用,那就叫做逃逸閉包
- 聲明閉包作為形參的函數時,可以在形參的類型之前寫上
@escaping
來明確閉包是允許逃逸的 - 逃逸閉包會在函數結束后才執行
自動閉包
- 一種自動創建的閉包,用于包裝函數參數的表達式
- 不接受任何參數,被調用時會返回被包裝在其中的表達式的值
- 在形參的類型之前加上
@autoclosure
關鍵字標識是一個自動閉包
Swift中閉包在官方系統庫中的應用函數
- sort —— 排序
var array: [String] = ["Animal", "Baby", "Apple", "Google", "Aunt"]
// 這種默認升序
array.sorted()
// 如果需要降序
array.sort { (str1, str2) -> Bool in
return str1 > str2
}
- forEach —— 遍歷
var array: [String] = ["Animal", "Baby", "Apple", "Google", "Aunt"]
// 遍歷
array.forEach { (str) in
print(str)
}
- filter —— 篩選
var array: [String] = ["Animal", "Baby", "Apple", "Google", "Aunt"]
// 篩選
let a = array.filter { (str) -> Bool in
str.starts(with: "A")
}
- map —— 變換
var array: [String] = ["Animal", "Baby", "Apple", "Google", "Aunt"]
// 閉包返回一個變換后的元素,接著組成一個新數組
let a = array.map { (str) -> String in
"Hello " + str
}
- reduce —— 合歸
var sum:[Int] = [11, 22, 33, 44]
var total = sum.reduce(0) { (result, num) -> Int in
return result + num
}
- allSatisfy —— 條件符合
// 判斷數組的所有元素是否全部大于85
let scores = [86, 88, 95, 92]
// 檢查序列中的所有元素是否滿足條件,返回Bool
let passed = scores.allSatisfy { $0 > 85 }
- compactMap —— 轉換
let arr: Array = [1, 2, 34, 5, 6, 7, 8, 12, 45, 6. 9]
// 返回操作的新數組(并不是篩選),數組,字典都可以使用
let compact = arr.compactMap({ $0%2 == 0})
- mapValues —— 轉換value
let dic = ["first":1, "second":2, "three":3, "four":4]
// 字典中的函數,對字典的value執行操作,返回改變value后新的字典
let mapValues = dic.mapValues({ $0 + 2 })
- compactMapValues —— 上面兩個的合并
let dic = ["first":1, "second":2, "three":3, "four":4, "five":"abc"]
// 將上述兩個方法的功能合并在一起,返回一個對value操作后的新字典,并且自動過濾不符合條件的鍵值對
let newDic = dic.compactMapValues({Int($0)})
first(where:) —— 篩選第一個符合條件的
last(where:) —— 篩選最后一個符合條件
var array: [String] = ["Animal", "Baby", "Apple", "Google", "Aunt"]
let elementF = array.first(where: { $0.hasPrefix("A") })
let elementL = array.last(where: { $0.hasPrefix("A") })
removeAll(where:) —— 刪除
// 高效根據條件刪除,比filter內存效率高,指定不想要的東西
var array: [String] = ["Animal", "Baby", "Apple", "Google", "Aunt"]
array.removeAll(where: { $0.hasPrefix("A") })
枚舉
Swift中的枚舉是一等類型,它可以像類和結構體一樣增加 屬性和方法
枚舉定義
enum Sex {
case male
case female
}
枚舉賦值
- 枚舉類型賦值可以是字符串/字符/整型/浮點型
- 如果有給枚舉類型賦值,則必須在枚舉類型后面明確具體類型
枚舉類型推斷
- 如果枚舉類型確定了,在訪問值的時候可以用 .值 來訪問
枚舉原始值
- 原始值區分大小寫
- 通過
rawValue
可以獲取原始值 - 通過
rawValue
返回的枚舉是一個可選型,因為原始值對應的枚舉不一定存在 - 如果指定第一個元素的原始值后,后面元素原始值默認+1,枚舉一定是Int類型
枚舉遍歷
enum Method: CaseIterable {
case Add, Sub, Mul, Div
}
for method in Method.allCases {
print(method)
}
枚舉的可選參數
// Swift5之后,可變參數的枚舉定義時,...改成了數組
enum ABC {
// case abc(argv: Int...)
case abc(argv: [Int])
}
func getABC() -> ABC {
return .abc(argv: [0, 1, 2, 3])
}
結構體
- 結構體(struct)是由一系列具有相同類型或不同類型的數據構成的數據集合
- 結構體是值類型(包括枚舉)它在代碼傳遞中總是會被拷貝
- 結構體既可以定義屬性又可以定義方法
- 常用的結構體比如:CGRect、CGSize、CGPoint
字符串,數組和字典的賦值與拷貝行為
- Swift 中的String,Array 和 Dictionary 類型是作為結構體來實現的,這意味著在它們被賦值到一個新的常量或變量,或它們本身被傳遞到一個方法中,其實是傳遞了拷貝。這里與OC中有明顯區別
類
- Swift雖然推薦面向協議編程,但其也是一門面向對象開發的語言
- 特征運算符
- 相同于(===)
- 不同于(!==)
- 繼承
- 重寫
override
- 防止被重寫
final
- 重寫
類與結構體對比
- 相同點
- 定義屬性
- 定義方法
- 定義構造函數(init函數)
- 可以被擴展
- 遵循協議
- 類有而結構體沒有的額外功能
- 繼承
- 類型轉換(子類 as 父類)
- 析構函數
- 引用計數
屬性與方法
類的屬性介紹有多種
- 存儲屬性:存儲實例的常量和變量
- 計算屬性:依賴于存儲屬性,通過計算得出來,它提供getter和setter間接訪問和設置值
- 類屬性:與整個類自身相關的屬性用static來修飾
- 懶加載屬性:用lazy修飾,必須進行初始化
總結
- 存儲屬性,最先被初始化
- 構造方法,僅次與存儲屬性調用,可以在這里對存儲屬性進行賦值
- 懶加載屬性、類屬性、全局屬性都是在第一次使用的時候初始化一次,以后調用都不再初始化
- 當懶加載屬性是基于一個存儲屬性計算的時候,切勿使用懶加載屬性,采用計算屬性
監聽屬性的改變
- Swift中可以通過屬性觀察者來監聽和響應屬性值的變化
- 定義觀察者
- willSet:在屬性值被存儲之前設置。此時新屬性值作為一個常量參數被傳入。該參數名默認為newValue,可以自定義
- didSet:在新屬性值被存儲后立即調用。與willSet相同,此時傳入的是屬性的舊值,默認參數名為oldValue,可以自定義
- willSet與didSet只有在屬性改變時才會調用,在初始化時不會去調用這些監聽的方法
值類型在實例方法中修改屬性和調用方法
- 值類型默認情況下,不能在實例方法中修改屬性
- 不能用self調用其他的函數
- 可以在函數前方一個
mutating
關鍵字來實現
類方法
- 在函數前使用
static
關鍵字(能在類、結構體中使用) - 在函數前使用
class
關鍵字(只能在類中使用)
class 和 static 總結
構造與析構函數
默認構造函數
- 默認構造函數就像一個沒有形參的實例方法,使用
init
關鍵字來寫
自定義構造函數
- 希望在創建一個對象時手動給屬性賦值(屬性的值是在外面傳進去的)
- 自定義構造函數和默認構造函數可以同時存在
Swift為類 類型定義了兩種構造函數以確保所有存儲屬性接收一個初始值,指定構造函數和便捷構造函數
- 指定構造函數是類的主要構造函數,指定構造函數可以初始化所有類引用的屬性,并且調用合適的父類構造函數來繼續這個初始化過程給父類鏈
- 便捷構造函數是次要的,可以在相同的類里定義一個便捷構造函數來調用一個指定構造函數給指定構造函數設置默認形式參數
// 類的指定構造函數
init(parameters) {
statements
}
// 便捷構造函數需用 convenience 修飾符放到 init 關鍵字前
convenience init(parameters) {
statements
self.init(parameters)
}
- 指定構造函數必須總是向上委托
- 便捷構造函數必須總是橫向委托
析構函數
- 當引用計數為0時,系統自動調用析構函數
deinit
(不可手動調用)
協議 protocol
協議可被類、結構體、或枚舉類型采納以提供所需功能的具體實現即遵循協議
擴展 extension
- 為現有的類、結構體、枚舉類型、協議添加新功能。擴展和Objective-C中的分類類似
- Swift中使用
extension
關鍵字實現擴展
面向協議編程
針對某個需要實現的功能,可以使用協議定義出接口,然后利用協議擴展提供默認的實現,需要這個功能,只需要聲明遵守這個協議即可,遵守某個協議的對象調用協議聲明的方法時,如果遵守者本身沒有提供實現,協議擴展提供的默認實現會被調用
泛型
類型約束 和 關聯類型
- 關聯類型通過
associatedtype
關鍵字指定
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
}
protocol SomeProtocol {
associatedtype Element: Equatable
func method1(element: Element)
}
異常
// 1、定義異常
enum FileReadError: Error {
case FileIsNull
case FileNotFound
}
// 2、讓方法拋出異常
func readFileContent(filePath: String) throws -> String {
if filePath == "" {
throw FileReadError.FileIsNull
}
if filePath != "/User/Desktop/123.plist" {
throw FileReadError.FileNotFound
}
return "123"
}
// 處理異常
do {
let result = try readFileContent(filePath: "abc")
} catch {
print(error) // 有一個隱藏參數 error
}
// defer關鍵字
Result
- 在Swift5中,新增了一個枚舉類型Result,使我們能夠更簡單、更清晰處理復雜代碼中的錯誤
// 使用Result處理異常如下
func readFileContent(filePath: String) -> Result<String, FileReadError> {
if filePath == "" {
return .failure(.FileIsNull)
}
if filePath != "/User/Desktop/123.plist" {
return .failure(.FileNotFound)
}
return .success("123")
}
// 調用
let result = readFileContent(filePath: "")
switch result {
case .failure(let error)
print(error)
case .success(let content)
print(content)
}
元類型、.self 與 Self
- 獲取對象類型:
type(of: )
語法 - 元類型:可以理解為類型的類型,可以通過
類型.Type
定義,可以修飾變量或常量,如何得到這種類型?需要通過類型.self
- Self大寫在定義協議的時候用的頻率很高,用于協議中限制相關的類型
@objc關鍵字
出于安全的考慮,需將暴露給Objective-C使用的如類、屬性和方法的聲明前面加上@objc
- #selector 中調用的方法需要在方法前聲明
@objc
- 協議的方法可選時,協議和可選方法前要用
@objc
聲明 - 用weak修飾的協議時 ,協議前面要用
@objc
聲明 - 類上加
@objcMembers
,則其及子類、擴展里的屬性和方法都會隱式的加上@objc
,如果部分不想加,可以用@nonobjc
修飾 - 擴展前加上
@objc
,那么里面的方法都會隱式加上@objc
where關鍵字
where關鍵字的含義和數據庫中差不多,用于條件篩選,在Swift中哪些地方用到,如下總結
- Switch case 分支
- for 循環
- protocol 協議
- Generic 泛型
- do catch 異常處理
Key Path
- 類似OC中的KVC
- 用于間接獲取/設置值
- 類必須繼承自NSObject,否則不能用
- 哪些屬性可以通過KeyPath操作,就需要在前面加上@objc
// Swift 3 之前
stu.value(forKey: "name")
stu.setValue("lisi", forKey: "name")
// Swift 3
stu.value(forKey: #keyPath(Student.sex))
stu.setValue("女", forKey: #keyPath(Student.sex))
// Swift 4
stu[keyPath: \Student.sex]
stu[keyPath: \Student.sex] = "女"
Codable協議
- JSON轉Model,以前可以利用KVC、NSJSONSerialization實現
- Swift 4之后推薦使用Codable協議,可以通過編碼和解碼實現互轉
訪問權限
-
open
和public
:允許被定義模塊中任意源文件訪問,也可以被另一模塊源文件通過導入該定義模塊來訪問 -
internal
:(默認)允許被定義模塊中的任意源文件訪問,但不能被該模塊之外的任何源文件訪問 -
fileprivate
:使用限制于當前定義的原文件中 -
private
:使用限制于封閉聲明中,比fileprivate更嚴格
注意
- 訪問權限可以修飾 類、方法、屬性等
- 在Swift4中,private的屬性作用域擴大到extension中,也就是說在extension中訪問屬性可以是fileprivate或private修飾的
學習參考
- 持續關注Swift之后發布的新版本,了解新特性,關注SwiftUI等
學習網址