YES
- 2014WWDC發布
常量和變量使用注意
- 在實際過程中,建議先定義常量,如果需要修改再改變為變量(更加安全)
- 可以在一行中聲明多個常量或者多個變量,用逗號隔開
var x = 0.0, y = 0.0, z = 0.0
var red, green, blue: Double
類型標注
- 如果要添加類型標注,需要在常量或者變量名后面加上一個冒號和空格,然后加上類型名稱
- 如果你需要使用與Swift保留關鍵字相同的名稱作為常量或者變量名,你可以使用反引號(`)將關鍵字包圍的方式將其作為名字使用。無論如何,你應當避免使用關鍵字作為常量或變量名,除非你別無選擇
整數范圍
- 你可以訪問不同整數類型的 min 和 max 屬性來獲取對應類型的最小值和最大值:
- 即使是在32位平臺上,Int 可以存儲的整數范圍也可以達到 -2,147,483,648 ~ 2,147,483,647 ,大多數時候這已經足夠大了
浮點數
- Double精確度很高,至少有15位數字,而Float只有6位數字。選擇哪個類型取決于你的代碼需要處理的值的范圍,在兩種類型都匹配的情況下,將優先選擇 Double。
- 整數和浮點數都可以添加額外的零并且包含下劃線,并不會影響字面量:
let paddedDouble = 000123.456
let oneMillion = 1_000_000
let justOverOneMillion = 1_000_000.000_000_1
- 通常來講,即使代碼中的整數常量和變量已知非負,也請使用Int類型
- 當用這種方式來初始化一個新的整數值時,浮點值會被截斷。也就是說 4.75 會變成 4,-3.9 會變成 -3。
類型推倒
- swift屬于強類型語言,任何一個標示符都有明確的類型
- 如果定義一個標示符時有直接進行賦值,那么標示符后面的類型可以省略,因為swift有類型推導,會自動根據后面的賦值來決定前面標示符的數據類型
- option + 鼠標左鍵 可以查看變量類型
類型別名
- 類型別名(type aliases)就是給現有類型定義另一個名字。你可以使用typealias關鍵字來定義類型別名
輸出常量和變量
- Swift 用字符串插值(string interpolation)的方式把常量名或者變量名當做占位符加入到長字符串中,Swift 會用當前常量或變量的值替換這些占位符。將常量或變量名放入圓括號中,并在開括號前使用反斜杠將其轉義
基本運算
- 沒有隱式轉換,必須保證類型一致
邏輯分支
- if else
- if 后面的小括號可以省略
- 判斷句必須有明確的真假
- 三目運算符 與OC 形同
- switch 語句
- switch后面的小括號可以省略
- case語句結束后的break也可以省略
- 如果需要穿透可以在case 語句后面跟上fallthrough
- case后面可以判斷多個條件,用 逗號 分隔
- switch可以判斷浮點型 字符串
- switch 可以判斷區間 開區間 0..<10 不包括 10 0...10 包括10
for循環
- forin
for i in 0..<10{
print(i)
}
for _ in 0..10{
}
while 循環
var c = 10
while c > 11 {
// 自增自減?????
print("大于0")
}
dowhile
repeat {
statements
} while condition
字符串
- String 是一個結構體,性能更高
- String支持直接遍歷
- swift提供了String 和 NSString之間的無縫轉換
let age = 28
let info = "my name is \(name),my age is \(age)"
// 拼接字符串時的格式化
let min = 2
let sec = 3
let timestring = String.init(format: "%02d:%02d", arguments:[min,sec])
let substr = (timestring as NSString).substring(to: 3)
數組
- 創建一個空數組 var someInts = Int
- 創建一個帶有默認值的數組
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles 是一種 [Double] 數組,等價于 [0.0, 0.0, 0.0]
- 通過兩個數組相加創建一個數組 +
- 使用布爾屬性isEmpty作為一個縮寫形式去檢查count屬性是否為0:
- 也可以使用append(_:)方法在數組后面添加新的數據項
- 如果我們同時需要每個數據項的值和索引值,可以使用enumerated()方法來進行數組遍歷。enumerated()返回一個由每一個數據項索引值和數據值組成的元組。我們可以把這個元組分解成臨時常量或者變量來進行遍歷:
for (index, value) in shoppingList. enumerated() {
print("Item \(String(index + 1)): \(value)")
}
集合
- 集合(Set)用來存儲相同類型并且沒有確定順序的值。當集合元素順序不重要時或者希望確保每個元素只出現一次時可以使用集合而不是數組
- var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// favoriteGenres 被構造成含有三個初始值的集合 - Swift 的Set類型沒有確定的順序,為了按照特定順序來遍歷一個Set中的值可以使用sorted()方法,它將返回一個有序數組,這個數組的元素排列順序由操作符'<'對元素進行比較的結果來確定.
- 集合運算
- 使用intersection(_:)方法根據兩個集合中都包含的值創建的一個新的集合。
- 使用symmetricDifference(_:)方法根據在一個集合中但不在兩個集合中的值創建一個新的集合。
- 使用union(_:)方法根據兩個集合的值創建一個新的集合。
- 使用subtracting(_:)方法根據不在該集合中的值創建一個新的集合。
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted()
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits. intersection(evenDigits).sorted()
// []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
// [1, 9]
oddDigits. symmetricDifference(singleDigitPrimeNumbers).sorted()
// [1, 2, 9]
let houseAnimals: Set = ["??", "??"]
let farmAnimals: Set = ["??", "??", "??", "??", "??"]
let cityAnimals: Set = ["??", "??"]
houseAnimals.isSubset(of: farmAnimals)
// true
farmAnimals.isSuperset(of: houseAnimals)
// true
farmAnimals.isDisjoint(with: cityAnimals)
// true
字典
- 創建字典
var dict = [String : AnyObject]()
- 和數組一樣,我們可以通過字典的只讀屬性count來獲取某個字典的數據項數量
- 使用布爾屬性isEmpty作為一個縮寫形式去檢查count屬性是否為0
- ,字典的updateValue(_:forKey:)方法可以設置或者更新特定鍵對應的值
- 如果我們只是需要使用某個字典的鍵集合或者值集合來作為某個接受Array實例的 API 的參數,可以直接使用keys或者values屬性構造一個新數組
let airportCodes = [String](airports.keys)
// airportCodes 是 ["YYZ", "LHR"]
let airportNames = [String](airports.values)
// airportNames 是 ["Toronto Pearson", "London Heathrow"]
- 遍歷字典 forin keys values
- 遍歷所有鍵值
for (key , value) in dictm{
}
- 合并字典:即使類型一致也不能相加合并
元組
*元組的基本寫法
let info = ("lili",18,1.88)
info.0
info.1
- 給元組的每個元素起一個別名
let info = (name :"lili",age : 18,height : 1.88)
info.name
info.age
- 元組中元素的別名,就是元組的名稱
let (name,age,height) = ("lili",18,1.88)
forin
- 使用 stride(from:to:by:) 函數跳過不需要的標記。
let minuteInterval = 5
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
// 每5分鐘呈現一個刻度線 (0, 5, 10, 15 ... 45, 50, 55)
}
可以在閉區間使用 stride(from:through:by:) 起到同樣作用:
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
// 每3小時呈現一個刻度線 (3, 6, 9, 12)
}
可選類型
定義可選類型
- 方法一 不常用
* var name : Optional<String> = nil
* var name : String? = nil
- 如果你聲明一個可選常量或者變量但是沒有賦值,它們會自動被設置為 nil
var surveyAnswer: String?
// surveyAnswer 被自動設置為 nil
- Swift 的 nil 和 Objective-C 中的 nil 并不一樣。在 Objective-C 中,nil 是一個指向不存在對象的指針。在 Swift 中,nil 不是指針——它是一個確定的值,用來表示值缺失。任何類型的可選狀態都可以被設置為 nil,不只是對象類型。
- 注意:強制解包是非常危險的,如果可選類型為nil,強制解包系統會崩潰
建議:在前置解包前,先對可選類型進行判斷,判斷是否nil
if name != nil {
print(name!)
}```
* 可選綁定
* 判斷name是否有值,如果沒有值,直接不執行{}
* 如果name有值,系統會自動將name進行強制解包,并且將解包后的結果賦值給name
if let name = name {
print(name)
}
* 你可以在可選綁定中使用常量和變量。如果你想在if語句的第一個分支中操作 actualNumber 的值,你可以改成 if var actualNumber,這樣可選類型包含的值就會被賦給一個變量而非常量。
### 隱式解析可選類型
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感嘆號來獲取值
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感嘆號
### 錯誤處理
* 當一個函數遇到錯誤條件,它能報錯。調用函數的地方能拋出錯誤消息并合理處理。
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
NO
### 字符串
* 判斷是否為""
if emptyString.isEmpty {
print("Nothing to see here")
}
* 字符串插值:您插入的字符串字面量的每一項都在以反斜線為前綴的圓括號中:
let multiplier = 3
let message = "(multiplier) times 2.5 is (Double(multiplier) * 2.5)"
// message 是 "3 times 2.5 is 7.5"
* 如果想要獲得一個字符串中Character值的數量,可以使用字符串的characters屬性的count屬性:
*
greeting[greeting.endIndex] // error
greeting.index(after: endIndex) // error
## Array
let oddNumbers = [1,3,5,7]
* 創建空數組
* var emptyDoubles : [Double] = []
* The full type name is also allowed
var emptyFloats: Array<Float> = Array()
* 如果你需要一個使用默認值提前初始化的數組
let digitCounts = Array(repeatElement(0, count: 10))
### 比較運算符
* 注意: Swift 也提供恒等(===
)和不恒等(!==
)這兩個比較符來判斷兩個對象是否引用同一個對象實例。
### 空合運算符
* a ?? b 表述的意思:a != nil ? a! : b
###元組比較
* Swift 標準庫只能比較七個以內元素的元組比較函數。如果你的元組元素超過七個時,你需要自己實現比較運算符。
* 因為 Int 和 String 類型的值可以比較,所以類型為 (Int, String) 的元組也可以被比較。
### 區間運算符
* 閉區間運算符: 閉區間運算符(a...b)定義一個包含從 a 到 b(包括 a 和 b)的所有值的區間。a 的值不能超過 b
* 半開區間運算符:半開區間運算符(a..<b)定義一個從 a 到 b 但不包括 b 的區間。 之所以稱為半開區間,是因為該區間包含第一個值而不包括最后的值。
### 邏輯組合運算符
* Swift 邏輯操作符 && 和 || 是左結合的,這意味著擁有多元邏輯操作符的復合表達式優先計算最左邊的子表達式。
* 數組遍歷
let streets = ["mahadun","newyork"]
for street in streets {
print("I don't live on \(street).")
}
* 判斷數組是否為空 isEmpty
if oddNumbers.isEmpty {
print("數組為空")
}else{
print("i know(oddNumbers.count) odd Numbers")
}
* 取到數組的第一個 最后一個元素 ,如果數組為空,返回nil
if let firstElement = oddNumbers.first , let lastelement = oddNumbers.last {
print(firstElement, lastelement, separator: ", ")
}
* 利用角標訪問數組元素,不在 0..<count 會報數組越界錯誤
* 增加或者刪除數組元素
var students = ["ben","ivy","jodell"]
// 添加單個元素 append(_:) 一次添加多個元素 append(contentsOf:)
students.append("maxi")
students.append(contentsOf: ["shaki","william"])
// 插入一個或多個元素
students.insert("liam", at: 3)
// 刪除元素
students.remove(at: 0)
// 刪除最后一個
students.removeLast()
* 修改元素
students[0] = "dddd"
**當數組元素的個數超過初始容量,數組會將分配更大的內存區域并將其元素復制到新存儲中。新存儲空間是舊存儲容量的倍數**
* 修改數組的副本copy
* 如果數據類型為int 結構體類型,修改原數組內容不會影響副本的內容
var numbers = [1, 2, 3, 4, 5]
var numbersCopy = numbers
numbers[0] = 100
print(numbers)
// Prints "[100, 2, 3, 4, 5]"
print(numbersCopy)
// Prints "[1, 2, 3, 4, 5]"
* 如果數組元素是對象
class InergerReference{
var value = 10
}
var firstIntegers = [InergerReference(),InergerReference()]
var secondInergers = firstIntegers
firstIntegers[0].value = 100
print(firstIntegers[0].value,secondInergers[0].value)
// 100 100
firstIntegers[0] = InergerReference()
print(firstIntegers[0].value,secondInergers[0].value)
// 10 100
* Array 與 NSArray的轉換 用as
## Dictionary
* 如何創建一個空字典
var emptyDict: [String : String] = [:]
* 通過key來取值 取出來的值我可選項
print(dictionary[200] ?? "")
if let message = dictionary[500]{
print(message)
}else{
print("meiyou改制")
}```
- 添加/修改鍵值對
dictionary[100] = "text" - 對已經存在的鍵賦值nil,改鍵值對會被移除
var interstingNumbers = ["primes" : [2,3,5,5,11,13],
"trianguler" : [1,3,4,56,6]]
for key in interstingNumbers.keys {
interstingNumbers[key]?.sort(by: >)
}
print(interstingNumbers["primes"] ?? "")
- 遍歷字典 每個字典都是無序的鍵值對的集合
var imagePaths = ["star": "/glyphs/star.png",
"portrait": "/images/content/portrait.jpg",
"spacer": "/images/shared/spacer.gif"]
for (key,value) in imagePaths {
print("\(key) : \(value)")
}
- 創建字典
var dict : [String : String] = Dictionary.init(minimumCapacity: 10)
dict["name"] = "lili"
dict.isEmpty
dict.count
- 訪問字典的鍵值對
imagePaths["star"]
imagePaths.index(forKey: "spacer")
imagePaths.first
imagePaths.keys
imagePaths.values
- 添加鍵值對
imagePaths.updateValue("falsh.png", forKey: "xxx")
imagePaths
- 移除鍵值對
imagePaths.removeValue(forKey: "flash")
imagePaths.removeAll()
- 遍歷字典
imagePaths.enumerated()
imagePaths.makeIterator()
imagePaths.underestimatedCount
imagePaths.first
可選項 Optional
- 可選項 ?
let shortForm: Int? = Int("33")
// 可選項包含兩個分支 Optional.some
let number: Int? = Optional.some(44)
let nonumber: Int? = Optional.none
print(nonumber == nil)
- 在很多情況下使用可選項都必須進行解包
let imagePaths = ["star": "/glyphs/star.png",
"portrait": "/images/content/portrait.jpg",
"spacer": "/images/shared/spacer.gif"]```
* 解包方式一: if let
if let starPath = imagePaths["star"] {
print("The star image is at '(starPath)'")
} else {
print("Couldn't find the star image")
}```
- ?? 當為nil時提供一個默認值
let defaultiamgepath = "/iamges/default.png"
let heartpath = imagePaths["heart"] ?? defaultiamgepath
- ?? 可以多個一起使用
let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultiamgepath
- 強制解包 解包失敗會崩潰
let num = Int("42")!
let isPng = imagePaths["star"]! .hasSuffix(".png")
switch 語句
- 不存在隱式的貫穿,不需要在 case 分支中顯式地使用break語句。
- 每一個 case 分支都必須包含至少一條語句。像下面這樣書寫代碼是無效的,因為第一個 case 分支是空的
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a": // 無效,這個分支下面沒有語句
case "A":
print("The letter A")
default:
print("Not the letter A")
}
// 這段代碼會報編譯錯誤
let anotherCharacter: Character = "a"
switch anotherCharacter {
case "a", "A":
print("The letter A")
default:
print("Not the letter A")
}
// 輸出 "The letter A
- 區間匹配 case 分支的模式也可以是一個值的區間。
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
var naturalCount: String
switch approximateCount {
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
// 輸出 "There are dozens of moons orbiting Saturn."
- case 分支的模式可以使用where語句來判斷額外的條件
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
// 輸出 "(1, -1) is on the line x == -y"
- 復合匹配 當多個條件可以使用同一種方法來處理時,可以將這幾種可能放在同一個case后面,并且用逗號隔開。當case后面的任意一種模式匹配的時候,這條分支就會被匹配。并且,如果匹配列表過長,還可以分行書寫
let someCharacter: Character = "e"
switch someCharacter {
case "a", "e", "i", "o", "u":
print("\(someCharacter) is a vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("\(someCharacter) is a consonant")
default:
print("\(someCharacter) is not a vowel or a consonant")
}
// 輸出 "e is a vowel"
控制轉移語句
- continue continue語句告訴一個循環體立刻停止本次循環,重新開始下次循環
- break語句會立刻結束整個控制流的執行。當你想要更早的結束一個switch代碼塊或者一個循環體時,你都可以使用break語句
*Switch 語句中的 break
當在一個switch代碼塊中使用break時,會立即中斷該switch代碼塊的執行,并且跳轉到表示switch代碼塊結束的大括號(})后的第一行代碼
- 貫穿
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
print(description)
// 輸出 "The number 5 is a prime number, and also an integer."
提前退出
- 像if語句一樣,guard的執行取決于一個表達式的布爾值。我們可以使用guard語句來要求條件必須為真時,以執行guard語句后的代碼。不同于if語句,一個guard語句總是有一個else從句,如果條件不為真則執行else從句中的代碼
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
檢測API的可用性 最后一個參數,*,是必須的,用于指定在所有其它平臺中,如果版本號高于你的設備指定的最低版本,if語句的代碼塊將會運行
if #available(iOS 10, macOS 10.12, *) {
// 在 iOS 使用 iOS 10 的 API, 在 macOS 使用 macOS 10.12 的 API
} else {
// 使用先前版本的 iOS 和 macOS 的 API
}
函數
- 多個相同類型參數 (可變參數一個函數最多只能擁有一個可變參數。)
func sumof(numbers:Int...) ->Int{
var sum = 0
for num in numbers {
sum += num
}
return sum
}
sumof(numbers: 1,2,3,5)```
* swift中的默認參數
func makeCoffee(coffeename: String = "藍山"){
print("我要喝(coffeename)咖啡")
}
makeCoffee()
makeCoffee(coffeename: "貓屎")
* 有參有返回值
func greet(name: String,day: String,number: Int) -> String{
return "Hello \(name),Today is\(day),\(number)"
}
print(greet(name: "zhangsan", day: "33", number: 23))
* 函數返回值為元組
可選元組類型如 (Int, Int)? 與元組包含可選類型如 (Int?, Int?) 是不同的.可選的元組類型,整個元組是可選的,而不只是元組中的每個元素值。
func calculate(scores: [Int]) -> (max: Int,min: Int,sum: Int){
var max = scores[0]
var min = scores[0]
var sum = 0
for num in scores {
if num > max {
max = num
}else if num < min{
min = num
}
sum += num
}
return (max,min,sum)
}
let statistics = calculate(scores: [3,4,5])
print(statistics.max)
print(statistics.0)
* 輸入輸出函數
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now (someInt), and anotherInt is now (anotherInt)")
// 打印 "someInt is now 107, and anotherInt is now 3"
* 函數作為返回類型
func makeIncrementer() -> ((Int,Int) -> Int){
func addNumber(num1: Int,num2: Int) -> Int{
return num1 + num2
}
return addNumber
}
let add = makeIncrementer()
add(3,4)
* **函數作為參數(重點)**
* 平方
func square(a: Float) -> Float{
return a * a
}
square(a: 2.0)```
- 立方
func cube(a: Float) -> Float{
return a * a * a
}
cube(a: 2.0)```
* 平均值
func averageSumm(num1: Float,num2: Float,function:(Float -> Float)) -> Float{
return (function(num1) + function(num2))/2
}```
- 函數的嵌套使用
func test(){
func demo(){}
demo()
}
test()
- 排序
var numbers = [1,3,2,5,63,7,0]
let sortNmbers = numbers.sorted(){
$0 < $1
}
print(sortNmbers)
類
class Strudent : NSObject{
// 定義存儲屬性
var name: String?
var age: Int = 0
var mathScore: Double = 0
var chineseScore: Double = 0
// 定義計算型屬性:通過別的方式計算得到結果的屬性
var averageScore: Double{
// 在swift中如果使用當前對象的屬性和方法不需要添加self
return (mathScore + chineseScore) * 0.5
}
// 定義類屬性: 類屬性是和整個類相關的屬性,而且是通過類名來進行訪問
static var courseCount: Int = 0
}
// 給類屬性賦值
Strudent.courseCount = 2
let stu = Strudent()
//stu.age = 20
stu.name = "zhangsan"
stu.mathScore = 50
stu.chineseScore = 60
stu.averageScore
// 解包
if let name = stu.name {
print(name)
}
// 可以利用KVC賦值
stu.setValue("lisi", forKey: "name")
- 監聽屬性變化
class Person: NSObject{
// var name: String?
var name: String?{
// 監聽屬性的變化
willSet {// 屬性即將改變時進行監聽
print("willsetlllll")
}
didSet {//屬性已經改變時進行監聽
print("didsettttt")
}
}
// 在構造函數中,如果沒有明確super.init(),那么系統會幫助調用super.init()
override init() {
super.init()
print("=====")
}
// 自定義構造函數
init(name: String) {
self.name = name
}
// 字典轉模型
init(dict: [String: AnyObject]) {
super.init()
setValuesForKeys(dict)
}
override func setValue(_ value: Any?, forUndefinedKey key: String) {
}
}
//let p1 = Person.init()
//let p2 = Person.init(name: "lisi")
let p3 = Person.init(dict: ["name": "wangwu" as AnyObject,
"age": 24 as AnyObject
])
print(p3.name ?? "")
便利構造函數
1.遍歷構造函數通常都是寫在extension里面
2.遍歷構造函數init前面需要加載convenience
3.在遍歷構造函數中需要明確的調用self.init()
extension UIButton {
convenience init(imageName: String,backGroundName: String) {
self.init()
setImage(UIImage.init(named:imageName), for:UIControlState())
setImage(UIImage.init(named: imageName + "_highlighted"), for: .highlighted)
setBackgroundImage(UIImage.init(named: backGroundName), for: .normal)
setBackgroundImage(UIImage.init(named: backGroundName + "highlighted"), for: .highlighted)
sizeToFit()
}
}
重寫一個類的description屬性
// MARK:- 重寫description
override var description: String{
return dictionaryWithValues(forKeys: ["access_token", "expires_in", "uid"]).description
}
點擊事件需要添加@objc fileprivate 非對外方法添加fileprivate
GCD 的使用
- 調度組:
let group = DispatchGroup.init()
// 入組
group.enter()
// 出租
group.leave()
// 通知
group.notify(queue: DispatchQueue.main) {
// 執行code
}
- 延遲執行
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 5) {
// 執行code
}
let queue = DispatchQueue.init(label: "")
queue.async {
<#code#>
}
隊列相關
// 創建隊列
let queue = DispatchQueue(label: "com.appcoda.myqueue")
// 指定優先級
let queue1 = DispatchQueue.init(label: "com.appcoda.myqueue1", qos:.background)
// 創建一個并發隊列
let queue1 = DispatchQueue.init(label: "com.appcoda.myqueue1",attributes:.concurrent)
//并發執行
queue1.async {
for i in 100 ..< 110{
print("??",i)
}
}
// 獲取全局隊列
DispatchQueue.global()
// 主隊列
DispatchQueue.main
workitem
// workitem
func useWorkItem() {
var value = 10
// 方法1
let workItem = DispatchWorkItem {
value += 5
}
workItem.perform()
// 方法2
let queue = DispatchQueue.global(qos: .utility)
queue.async(execute: workItem)
//通知
workItem.notify(queue: DispatchQueue.main) {
print("value = ", value)
}
}
閉包
- 閉包可以捕獲和存儲其所在上下文中任意常量和變量的引用。被稱為包裹常量和變量。 Swift 會為你管理在捕獲過程中涉及到的所有內存操作。
{ (parameters) -> returnType in
statements
}
枚舉
- 應該以一個大寫字母開頭。給枚舉類型起一個單數名字而不是復數名字
與 C 和 Objective-C 不同,Swift 的枚舉成員在被創建時不會被賦予一個默認的整型值。在上面的CompassPoint例子中,north,south,east和west不會被隱式地賦值為0,1,2和3。相反,這些枚舉成員本身就是完備的值,這些值的類型是已經明確定義好的CompassPoint類型。
enum CompassPoint {
case north
case south
case east
case west
}
- 多個成員值可以出現在同一行上,用逗號隔開:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
- 使用switch語句匹配枚舉值
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// 打印 "Watch out for penguins”
判斷一個枚舉類型的值時,switch語句必須窮舉所有情況。如果忽略了.west這種情況,上面那段代碼將無法通過編譯,因為它沒有考慮到CompassPoint的全部成員。強制窮舉確保了枚舉成員不會被意外遺漏。
- 當不需要匹配每個枚舉成員的時候,你可以提供一個default分支來涵蓋所有未明確處理的枚舉成員
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// 打印 "Mostly harmless”
- 原始值的隱式賦值
- 在使用原始值為整數或者字符串類型的枚舉時,不需要顯式地為每一個枚舉成員設置原始值,Swift 將會自動為你賦值。
例如,當使用整數作為原始值時,隱式賦值的值依次遞增1。如果第一個枚舉成員沒有設置原始值,其原始值將為0 - 當使用字符串作為枚舉類型的原始值時,每個枚舉成員的隱式原始值為該枚舉成員的名稱。
- 使用枚舉成員的rawValue屬性可以訪問該枚舉成員的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder 值為 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection 值為 "west"
- 遞歸枚舉
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
類和結構體
- 結構體總是通過被復制的方式在代碼中傳遞,不使用引用計數。
class SomeClass {
// 在這里定義類
}
struct SomeStructure {
// 在這里定義結構體
}
- 構造器最簡單形式是在結構體或者類型名稱后跟隨一對空括號
- 與 Objective-C 語言不同的是,Swift 允許直接設置結構體屬性的子屬性。上面的最后一個例子,就是直接設置了someVideoMode中resolution屬性的width這個子屬性,以上操作并不需要重新為整個resolution屬性設置新值。
- 結構體類型的成員逐一構造器所有結構體都有一個自動生成的成員逐一構造器,用于初始化新結構體實例中成員的屬性。新實例中各個屬性的初始值可以通過屬性的名稱傳遞到成員逐一構造器之中
let vga = Resolution(width:640, height: 480)
- 結構體和枚舉是值類型
值類型被賦予給一個變量、常量或者被傳遞給一個函數的時候,其值會被拷貝。 - 恒等運算符
- 因為類是引用類型,有可能有多個常量和變量在幕后同時引用同一個類實例。(對于結構體和枚舉來說,這并不成立。因為它們作為值類型,在被賦予到常量、變量或者傳遞到函數時,其值總是會被拷貝。)
- 等價于(===)
不等價于(!==)
- 字符串、數組、和字典類型的賦值與復制行為
- Swift 中,許多基本類型,諸如String,Array和Dictionary類型均以結構體的形式實現。這意味著被賦值給新的常量或變量,或者被傳入函數或方法中時,它們的值會被拷貝。Objective-C 中NSString,NSArray和NSDictionary類型均以類的形式實現,而并非結構體。它們在被賦值或者被傳入函數或方法時,不會發生值拷貝,而是傳遞現有實例的引用。
屬性
- 存儲屬性只能用于類和結構體,計算屬性可以用于類結構體和枚舉.
- 如果創建了一個結構體的實例并將其賦值給一個常量,則無法修改該實例的任何屬性,即使有屬性被聲明為變量也不行:
let rangeOfFourItems = FixedLengthRange(firstValue: 0, length: 4)
// 該區間表示整數0,1,2,3
rangeOfFourItems.firstValue = 6
// 盡管 firstValue 是個變量屬性,這里還是會報錯
這種行為是由于結構體(struct)屬于值類型。當值類型的實例被聲明為常量的時候,它的所有屬性也就成了常量。
屬于引用類型的類(class)則不一樣。把一個引用類型的實例賦給一個常量后,仍然可以修改該實例的變量屬性
- 懶加載屬性lazy
必須將延遲存儲屬性聲明成變量(使用 var 關鍵字),因為屬性的初始值可能在實例構造完成之后才會得到。而常量屬性在構造過程完成之前必須要有初始值,因此無法聲明成延遲屬性。注意
如果一個被標記為 lazy 的屬性在沒有初始化時就同時被多個線程訪問,則無法保證該屬性只會被初始化一次。
- 計算型屬性:
struct AlternativeRect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
- 只讀計算屬性只有 getter 沒有 setter 的計算屬性就是只讀計算屬性。只讀計算屬性總是返回一個值,可以通過點運算符訪問,但不能設置新的值
注意
必須使用 var 關鍵字定義計算屬性,包括只讀計算屬性,因為它們的值不是固定的。let 關鍵字只用來聲明常量屬性,表示初始化后再也無法修改的值
- 只讀計算屬性的聲明可以去掉 get 關鍵字和花括號:
struct Cuboid {
var width = 0.0, height = 0.0, depth = 0.0
var volume: Double {
return width * height * depth
}
}
let fourByFiveByTwo = Cuboid(width: 4.0, height: 5.0, depth: 2.0)
print("the volume of fourByFiveByTwo is \(fourByFiveByTwo.volume)")
// 打印 "the volume of fourByFiveByTwo is 40.0"
- 屬性觀察器
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
全局變量和局部變量
- 全局變量是在函數、方法、閉包或任何類型之外定義的變量。局部變量是在函數、方法或閉包內部定義的變量。
注意全局的常量或變量都是延遲計算的,跟[延遲存儲屬性]相似,不同的地方在于,全局的常量或變量不需要標記lazy
修飾符。局部范圍的常量或變量從不延遲計算。
方法
- 類、結構體、枚舉都可以定義實例方法;類、結構體、枚舉也可以定義類型方法
結構體和枚舉能夠定義方法是 Swift 與 C/Objective-C 的主要區別之一。
實際上,你不必在你的代碼里面經常寫self。不論何時,只要在一個方法中使用一個已知的屬性或者方法名稱,如果你沒有明確地寫self,Swift 假定你是指當前實例的屬性或者方法。
在實例方法中修改值類型
結構體和枚舉是值類型。默認情況下,值類型的屬性不能在它的實例方法中被修改。
但是,如果你確實需要在某個特定的方法中修改結構體或者枚舉的屬性,你可以為這個方法選擇可變(mutating)行為,然后就可以從其方法內部改變它的屬性;并且這個方法做的任何改變都會在方法執行結束時寫回到原始結構中。方法還可以給它隱含的self屬性賦予一個全新的實例,這個新實例在方法結束時會替換現存實例。
要使用可變方法,將關鍵字mutating 放到方法的func關鍵字之前就可以了
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveByX(2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// 打印 "The point is now at (3.0, 4.0)"
注意,不能在結構體類型的常量(a constant of structure type)上調用可變方法,因為其屬性不能被改變,即使屬性是變量屬性,
let fixedPoint = Point(x: 3.0, y: 3.0)fixedPoint.moveByX(2.0, y: 3.0)// 這里將會報告一個錯誤
- 在可變方法中給 self 賦值
可變方法能夠賦給隱含屬性self一個全新的實例
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
類型方法
- 方法的func關鍵字之前加上關鍵字static,來指定類型方法。類還可以用關鍵字class來允許子類重寫父類的方法實現。
注意
在 Objective-C 中,你只能為 Objective-C 的類類型(classes)定義類型方法(type-level methods)。在 Swift 中,你可以為所有的類、結構體和枚舉定義類型方法。每一個類型方法都被它所支持的類型顯式包含
下標
Swift 的Dictionary類型的下標接受并返回可選類型的值。上例中的numberOfLegs字典通過下標返回的是一個Int?或者說“可選的int”。Dictionary類型之所以如此實現下標,是因為不是每個鍵都有個對應的值,同時這也提供了一種通過鍵刪除對應值的方式,只需將鍵對應的值賦值為nil即可。
繼承
- 在 Swift 中,繼承是區分「類」與其它類型的一個基本特征。
- 可以為類中繼承來的屬性添加屬性觀察器,這樣一來,當屬性值改變時,類就會被通知到。可以為任何屬性添加屬性觀察器,無論它原本被定義為存儲型屬性還是計算型屬性
- 子類
class SomeClass: SomeSuperclass {
// 這里是子類的定義
}
重寫
- 如果要重寫某個特性,你需要在重寫定義的前面加上override關鍵字。這么做,你就表明了你是想提供一個重寫版本,而非錯誤地提供了一個相同的定義。意外的重寫行為可能會導致不可預知的錯誤,任何缺少override關鍵字的重寫都會在編譯時被診斷為錯誤。
在方法someMethod()的重寫實現中,可以通過super.someMethod()來調用超類版本的someMethod()方法。
在屬性someProperty的 getter 或 setter 的重寫實現中,可以通過super.someProperty來訪問超類版本的someProperty屬性。
在下標的重寫實現中,可以通過super[someIndex]來訪問超類版本中的相同下標。
注意
你不可以為繼承來的常量存儲型屬性或繼承來的只讀計算型屬性添加屬性觀察器。這些屬性的值是不可以被設置的,所以,為它們提供willSet或didSet實現是不恰當。
此外還要注意,你不可以同時提供重寫的 setter 和重寫的屬性觀察器。如果你想觀察屬性值的變化,并且你已經為那個屬性提供了定制的 setter,那么你在 setter 中就可以觀察到任何值變化了。
防止重寫
- 你可以通過把方法,屬性或下標標記為final來防止它們被重寫,只需要在聲明關鍵字前加上final修飾符即可(例如:final var,final func,final class func,以及final subscript)。
構造過程
構造過程是使用類、結構體或枚舉類型的實例之前的準備過程。在新實例可用前必須執行這個過程,具體操作包括設置實例中每個存儲型屬性的初始值和執行其他必須的設置或初始化工作。
類和結構體在創建實例時,必須為所有存儲型屬性設置合適的初始值。存儲型屬性的值不能處于一個未知的狀態。
你可以在構造器中為存儲型屬性賦初值,也可以在定義屬性時為其設置默認值。
注意
當你為存儲型屬性設置默認值或者在構造器中為其賦值時,它們的值是被直接設置的,不會觸發任何屬性觀察者。
- 不帶外部名的構造器參數如果你不希望為構造器的某個參數提供外部名字,你可以使用下劃線(_)來顯式描述它的外部名,以此重寫上面所說的默認行為
*構造過程中常量屬性的修改 你可以在構造過程中的任意時間點給常量屬性指定一個值,只要在構造過程結束時是一個確定的值。一旦常量屬性被賦值,它將永遠不可更改。
注意
對于類的實例來說,它的常量屬性只能在定義它的類的構造過程中修改;不能在子類中修改。
- 默認構造器 ()
- 結構體的逐一成員構造器 如果結構體沒有提供自定義的構造器,它們將自動獲得一個逐一成員構造器,即使結構體的存儲型屬性沒有默認值。
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
類的繼承和構造過程
- 類里面的所有存儲型屬性——包括所有繼承自父類的屬性——都必須在構造過程中設置初始值。
- 構造器的繼承和重寫跟 Objective-C 中的子類不同,Swift 中的子類默認情況下不會繼承父類的構造器。Swift 的這種機制可以防止一個父類的簡單構造器被一個更精細的子類繼承,并被錯誤地用來創建子類的實例。
注意
子類可以在初始化時修改繼承來的變量屬性,但是不能修改繼承來的常量屬性。
?### 析構 deinit
- 在類的定義中,每個類最多只能有一個析構器,而且析構器不帶任何參數
deinit {
// 執行析構過程
}
擴展
- 添加計算型屬性和計算型類型屬性
- 定義實例方法和類型方法
- 提供新的構造器
- 定義下標
- 定義和使用新的嵌套類型
- 使一個已有類型符合某個協議
extension SomeType {
// 為 SomeType 添加的新功能寫到這里
}
extension SomeType: SomeProtocol, AnotherProctocol {
// 協議實現寫到這里
}
注意
擴展可以添加新的計算型屬性,但是不可以添加存儲型屬性,也不可以為已有屬性添加屬性觀察器。
閉包
- 閉包表達式語法
{ (parameters) -> returnType in
statements
}
- 閉包的函數體部分由關鍵字in引入.該關鍵字表示閉包的參數和返回值類型定義已經完成,閉包函數體即將開始.
- 單表達式閉包隱式返回單行表達式閉包可以通過省略 return 關鍵字來隱式返回單行表達式的結果
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
- 尾隨閉包
func someFunctionThatTakesAClosure(closure: () -> Void) {
// 函數體部分
}
// 以下是不使用尾隨閉包進行函數調用
someFunctionThatTakesAClosure(closure: {
// 閉包主體部分
})
// 以下是使用尾隨閉包進行函數調用
someFunctionThatTakesAClosure() {
// 閉包主體部分
}
- 值捕獲閉包可以在其被定義的上下文中捕獲常量或變量。即使定義這些常量和變量的原作用域已經不存在,閉包仍然可以在閉包函數體內引用和修改這些值。
- 閉包是引用類型無論你將函數或閉包賦值給一個常量還是變量,你實際上都是將常量或變量的值設置為對應函數或閉包的引用
- 逃逸閉包:當一個閉包作為參數傳到一個函數中,但是這個閉包在函數返回之后才被執行,我們稱該閉包從函數中逃逸。當你定義接受閉包作為參數的函數時,你可以在參數名之前標注 @escaping,用來指明這個閉包是允許“逃逸”出這個函數的。
- 逃逸閉包中需要顯式的引用self
- 在非逃逸閉包中可以隱式的引用self