前言:
1.此文中的語法會根據Swift的升級變動而更新。
2.如果需要請移步 -> swift2.2 語法(上)、swift 2.2語法(下)
函數
和C語言一樣,swift也有函數,性質和我們熟悉的Objective-C的方法相當
-
格式:
func 函數名(參數列表) -> 返回值類型 { 代碼塊 // 如果設置了返回值類型,就需要返回相應的返回值 return 返回值 }
- func是swift內的關鍵字,參數列表內多個參數之間用 "," 隔開,可以沒有參數
- "->" 指向返回值類型
- 當然,如果函數沒有返回值(返回值為Void),那么 "-> 返回值類型" 這部分可以省略不寫(默認就是沒有返回值)
-
函數幾種定義方式:
// 定義沒有參數,沒有返回值的函數 // 標準寫法 func test1() -> Void { print("沒有參數沒有返回值的函數") } // 簡寫方式一 func test2() -> () { print("沒有參數沒有返回值的函數") } // 簡寫方式二 func test3() { print("沒有參數沒有返回值的函數") } // 定義有參數,沒有返回值的函數 // 標準寫法 func test1(num : Int) -> Void { print("有參數沒有返回值的函數") } // 簡寫方式一 func test2(num : Int) -> () { print("有參數沒有返回值的函數") } // 簡寫方式二 func test3(num : Int) { print("有參數沒有返回值的函數") } // 定義沒有參數,有返回值的函數 // 標準寫法 func test1() -> Int { return 0 } // 定義有返回值,有參數的函數 // 標準寫法 func test1(num1 : Int, num2 : Int) -> Int { return num1 + num2 } // 定義一個有參數,且有多個返回值的函數 // 標準寫法 func test1(nums : [String]) -> (strCount : Int, spellString : String) { var strCount = 0 var spellString = "" for num in nums { strCount++ spellString = spellString + num } return (strCount, spellString) } // 調用函數 print(test1(["abc", "def", "ghi"]))
-
函數的外部參數和內部參數
- 內部參數:在函數內部可以看到的參數
- 外部參數:在函數外面可以看到的參數
- 從第二個參數開始,參數名稱即是內部參數也是外部參數(默認)
// 先來定義一個擁有多個參數的函數 func test1(num1 : Int, num2 : Int, num3 : Int) -> Void { print(num1, num2, num3) } // 在調用函數的時候可以看下區別 /* * 第一個函數沒有跟上我們定義參數時所給的標簽 * 后面的所有函數都有我們定義參數時所給的標簽 * 原因:從第二個參數開始,參數名稱即是內部參數也是外部參數(默認) * 也就是說第一個參數默認為內部參數,所以不會顯示標簽 */ test1(5, num2: 6, num3: 7)
- 如果第一個參數也想有外部參數,可以設置標簽:在變量名前增加標簽
// 先來定義一個擁有多個參數的函數,但是這次我們要讓第一個參數也有外部參數 func test1(num1 num1: Int, num2 : Int, num3 : Int) -> Void { print(num1, num2, num3) } // 現在再調用函數可以看到第一個參數也變擁有外部參數 test1(num1: 5, num2: 6, num3: 7)
- 在參數名稱前加 "_" ,表示不需要外部參數
// 先來定義一個擁有多個參數的函數,這次我們讓所有參數都為內部參數 func test1 (num1 : Int, _ num2 : Int, _ num3 : Int) -> Void { print(num1, num2, num3) } // 現在調用函數就會發現所有的參數都成為內部參數了 test1(5, 6, 7)
-
函數重載
- 函數名稱相同,但參數不同,可以稱為函數重載
- 參數擁有內部參數或外部參數不同,也被認定為參數不同
// 函數重載例:定義三個函數,函數名相同但函數的參數不同 // 無返回值,第一個參數為內部參數,第二個參數同時擁有外部和內部參數 func test(num1 : Int, num2 : Int) -> Void { print("第一個函數") } // 無返回值,且都同時沒有外部參數 func test(num1 : Int, _ num2 : Int) -> Void { print("第二個函數") } // 無返回值,但同時擁有外部和內部參數 func test(num1 num1 : Int, num2 : Int) -> Void { print("第三個函數") } // 調用函數 test(1, num2: 2) test(1, 2) test(num1: 1, num2: 2)
-
默認參數
- 在一些特定情況下,調用函數時如果沒有傳入具體參數,可以使用默認參數代替(相當于我們在定義參數時就給參數一個默認值)
// 默認參數 func test(num : Int = 5) -> Int { return num } // 調用參數不給值,返回的的就是默認值 “5” test()
-
可變參數
- swift函數的參數
個數
可以變化,可以接收不確定數量
的輸入類型參數 - 需要注意的是,這些參數
必須
具有相同的類型 - 方法:在
參數類型
后面加 "..." 表示參數為可變參數
// 定義函數,參數為可變參數 func test(nums : Int...) -> Int { var sum : Int = 0 // 遍歷內部元素 for temp in nums { sum += temp } return sum } // 調用函數,結果為202 test(20, 15, 35, 32, 100)
- swift函數的參數
-
指針傳遞(引用類型)
- 函數的參數是通過值傳遞的方式,如果想改變外面的變量,需要傳遞變量地址(默認)
- swift提供了 "inout" 關鍵字來幫助我們實現
- 需要注意的都是,操作的必須是變量,因為需要在內部改變值
// 比如C語言中常見的問題:交換2個變量的值 // 先來看看正常的值傳遞 // 定義需要交換值的2個變量a,b var a = 6 var b = 9 // 值傳遞方式 func test(var num1: Int, var num2: Int) -> (num1 : Int, num2 : Int) { let temp = num1 num1 = num2 num2 = temp return (num1,num2) } // 調用函數(交換的只是函數內部參數的值,而a,b的值并沒變) test(a, num2: b) // 結果 9 6 print(a, b) // 結果 6 9 // 通過上面的方式可以明顯看出值傳遞并不能真實轉換外部變量的值,在swift中我們可以通 過"inout"關鍵字來講外部變量的值傳給函數參數,再改變其值 func test(inout num1 : Int, inout num2 : Int) -> (num1 : Int, num2 : Int) { let temp = num1 num1 = num2 num2 = temp return (num1, num2) } // 調用函數(因為傳入的是a,b的變量地址,等于拿到了外部變量,函數內部操作的num1,num2可以看成a,b,因為他們的指針指向了a,b) test(&a, num2: &b) print(a,b)
-
函數嵌套使用
- swift中函數可以嵌套使用
- 雖然可以嵌套使用,但是這種做法降低了可讀性,所以盡量不要這樣做
// 函數嵌套 let a = 100 let b = 35 func test() { func sum(num1 : Int, num2 : Int) { print("和為\(num1 + num2)") } print("test函數") sum(a, num2: b) } // 調用函數 test() // 先調用test函數,再調用sum函數
注意:無法調用sum函數,因為它是test函數的一部分
-
函數類型
- 每個函數都有屬于自己的類型,由函數的參數類型和返回值類型組成
// 函數類型:定義2個函數,且都為 (String, String) -> (String) 類型 func test1(name : String, city : String) -> String { return name + city } func test2(tiele : String, iconUrl : String) -> String { return "圖片名稱:\(tiele)圖片地址為:\(iconUrl)" }
- 每個函數都有屬于自己的類型,由函數的參數類型和返回值類型組成
將函數作當成變量傳遞
// 根據函數類型定義變量并將函數傳遞給變量
var tempFunction : (String, String) -> String = test1
// 使用變量名調用函數
tempFunction("sd", "lw")
- 將函數當成參數使用
// 將函數當成參數使用
func test3(str1 : String, str2 : String, tempFunction : (String, String) -> String) {
print(tempFunction(str1, str2))
}
// 調用函數
test3("aa", str2: "bb", tempFunction: test1) // 結果 aabb
- 函數當成返回值
// 函數作為方法返回值
/**
* 判斷一數是否為負數,是負數返回0,不是則返回原來的值
*/
// 正數調用此函數
func positive(num : Int) -> Int {
return num
}
// 負數調用此函數
func negative(num : Int) -> Int {
return 0
}
// 將函數作為返回值
func test(num : Int) -> (Int) -> Int {
// 如果函數小于1就是負數,返回negative函數,如果大于1,則是正數,返回positive函數
return num < 0 ? negative : positive
}
// 獲取返回值(函數)
let function = test(-1) // 因為是負數,所以返回negative函數
// 調用方法(再將-1傳入返回的函數)
function(-1) // 結果為 0
枚舉類型
枚舉用來定義一組通用類型的相關值,可以讓達到讓使用者按照設計者的規范使用特定的值
swift的枚舉非常靈活,可以給枚舉成員提供一個字符串,一個字符,一個整型或浮點值,不必給每個枚舉成員提供值
C語言和OC里面枚舉指定的一組相關成員為整型
-
枚舉類型定義
- 使用
enum
關鍵詞,將定義放在{}內 - 使用
case
關鍵詞定義新枚舉成員 - 需要注意的是,swift的枚舉在被創建的時候不會像C和OC那樣賦予默認整型值
// 枚舉定義 enum testType { case testTypeOne case testTypeTwo case testTypeThree } // 當然也可以簡寫成下面的方式(在枚舉成員特別多的情況下很好用) enum testType { case testTypeOne, testTypeTwo, testTypeThree }
- 使用
枚舉類型賦值
-
枚舉類型賦值可以是整型、浮點型、字符、字符串
- 如果有給枚舉類型賦值,必須在枚舉類型后面明確類型使用的類型
// 枚舉類型賦值 enum testType1 : Int { case One = 1 case Two = 2 case Three = 3 } // 或 enum testType2 : Int{ case One = 1, Two, Three }
- 如果有給枚舉類型賦值,必須在枚舉類型后面明確類型使用的類型
枚舉類型使用
// 枚舉類型賦值(整型)
enum testType1 : Int {
case One = 1
case Two
case Three
}
// 枚舉類型賦值(字符串)
enum testType2 : String{
case One = "a", Two = "b", Three
}
let j = testType1(rawValue: 2) // 獲取到枚舉成員Two
// 結果為Two
if let j = j {
switch j {
case .One:
print("One")
case .Two:
print("Two")
case .Three:
print("Three")
}
}
let i = testType2(rawValue: "a") // 獲取到枚舉成員One
// 結果為One
if let i = i {
switch i {
case .One:
print("One")
case .Two:
print("Two")
case .Three:
print("Three")
}
}
注意:
1.如果明確了類型為整型且未設置初始值,那么由0開始依次遞增(默認)
2.如果明確了類型為整型但設置了初始值,就由初始值依次遞增
結構體(struct)
結構體是一種數據結構
結構體在方法(函數)中傳遞時時值傳遞,是值類型
結構體是由一組相同類型或不同類型的數據組成的數據集合
在特定情況下使用結構體,能使我們的代碼結構更清晰
定義結構體格式
struct 結構體名稱 {
屬性
}
- 結構體使用
/**
* 在手勢開發過程中,我們需要監聽手指移動的位置,這邊我們來判斷手指移動的開始位置和結束位置距離是否大于50
*/
// 初始化結構體
struct touchPoint {
var x : Double
var y : Double
}
// 定義函數
func Range(point : touchPoint) -> Bool {
let tempX = point.x - startX
let tempY = point.y - startY
// sqrt(n)用來計算n的平方根
// pow(x, n)用來計算x的n次方
let range = sqrt(pow(tempX, 2) + pow(tempY,2))
return range > 50
}
// 創建結構體
let start = touchPoint(x:53.0, y:21.0)
let end = touchPoint(x: 120.0, y: 320.0)
// 調用函數
Range(point) // 結果:true
- 結構體增強
- 擴充構造函數
- 默認情況下創建touchPoint時使用touchPoint(x:,y:)
- 為了更加靈活地使用結構體,swift支持對構造函數進行擴充
- 在擴充的構造函數中必須保證成員變量有值
- 擴充的構造函數會覆蓋原有的構造函數
- 擴充構造函數
截止至:5.17 —— 1:00 5.18繼續
類的定義
swift也是面向對象開發的語言,而面向對象的基礎是類,由類產生對象
在swift中定義類使用
class
關鍵字-
類的注意點:
- 定義的類可以沒有父類,也就是這個類就是rootClass
- 一般情況下,定義類的時候,繼承自NSObject(但并不是OC中的NSObject)
格式:class 類名 : 父類 {
屬性,方法
}-
類的屬性
- siwft中類的屬性分為:
- 存儲屬性: 存儲實例常量和變量的屬性
- 類屬性:與整個類自身相關的屬性
- 計算屬性:通過某些算法計算出來的屬性
- siwft中類的屬性分為:
-
存儲屬性
- 存儲屬性是最簡單的屬性,作為類實例的一部分,用于存儲變量和常量
- 可給存儲屬性提供默認值,也可以在初始化方法中對其進行初始化
// 定義person類 class person: NSObject { // 定義存儲屬性 var name : String? // 名字 var age : Int = 0 // 年齡 } // 創建person對象 let ps = person() // 給存儲屬性賦值 ps.name = "stephen" ps.age = 23
-
類屬性
- 類屬性是與類相關聯的屬性,但不是與類實例相關聯的屬性
- 所有的實例和類共有一份類屬性,所以只要有某一處修改,這個類的屬性就會被修改
- 類屬性的設置和修改必須通過類來完成
- 類屬性使用
static
來修飾
// 定義person類 class person: NSObject { // 定義存儲屬性 var name : String? // 名字 var age : Int = 0 // 年齡 // 類屬性 static var nickName : String? } // 設置類屬性值 person.nickName = "laoWang" // 打印 print(person.nickName!) // 結果:laoWang
-
計算屬性
- 計算屬性并不會存儲實際的值,而是提供一個getter和一個setter(可選類型)間接獲取和設置其它屬性
- 一般情況下,只會提供getter方法,這種情況下的計算屬性為只讀屬性,可以省略 get{}
// 定義person類 class person: NSObject { // 定義存儲屬性 var foodIntake : Double = 0.0 // 人的食量(一頓吃幾碗) var consume : Double = 0.0 // 消耗量 // 定義計算屬性(差值) var difference : Double { get { return (foodIntake - consume) } // newValue是系統自動分配的變量名,內部用來存儲新的值 // 里面放的是get方法里面計算的值 set { self.difference = newValue } } } // 創建person對象 let ps = person() ps.foodIntake = 50 // 吃的有點多肯定比我胖 ps.consume = 25 // 消耗這么多驚呆小伙伴,看來是個肌肉男 // 打印 print(ps.difference) // 結果 25.0
類的屬性改變監聽
- OC里面我們可以重寫set方法來監聽屬性值的改變
- swift則需要通過屬性觀察者來監聽和相應屬性值的變化
- 一般只會監聽存儲屬性和類屬性改變,計算屬性我們不需要定義屬性觀察者,因為我們可以在計算屬性的setter中直接觀察并響應其值得變化
- swift使用下面的觀察方法定義觀察者
- willSet:在屬性值被存儲之前設置(新值會通過常量參數的方式傳入,這個參數就是
newValue
,我們可以給這個參數定義參數名,但一般保持默認) - didSet:新值被存儲后立即調用這個方法,和willSet相同,這時傳入的值是屬性的舊值,默認的參數名叫
oldValue
- willSet和didSet只在屬性第一次被設置的時候才會調用,初始化的時候并不會調用這2個監聽方法
- willSet:在屬性值被存儲之前設置(新值會通過常量參數的方式傳入,這個參數就是
class person: NSObject {
var name : String? {
willSet (newValue) { // 屬性即將改變時調用
// 會傳入系統默認的屬性newValue,用來存儲新值
print("name:\(name), newValue:\(newValue)") // 結果:name:nil, newValue:Optional("laoWang")
}
didSet (oldValue) {
// 會傳入系統默認的屬性oldValue,用來存儲舊值
print("name:\(name), oldValue\(oldValue)") // 結果:name:Optional("laoWang"), oldValuenil
}
}
}
// 創建person對象
let ps : person = person()
ps.name = "laoWang"
類的構造函數
默認情況下創建一個類的時候,就會調用一個構造函數
就算我們沒有編寫任何構造函數,編譯器也會提供一個默認的構造函數
如果類繼承自NSObject,可以對父類的構造函數進行重寫
構造函數和OC中的初始化方法init:相似
-
構造函數使用
- 類的屬性必須有值
- 如果在定義時沒有初始化值,可以在構造函數內進行賦值
class person: NSObject { var name : String // 因為繼承自NSObject,我們就重寫(父類)的構造方法 // override關鍵字表示調用父類方法 override init() { // 在初始化name屬性時沒有給它賦值,所以可以在構造函數里面進行賦值 name = "laoWang" } } // 創建person對象 let ps : person = person() print(ps.name) // 結果:laoWang
-
初始化時給屬性賦值
- 一般我們在創建一個對象的時候就會同時給屬性賦值,這時候我們可以自定義構造函數
- 需要注意的:如果我們自定義了構造函數,就會覆蓋init:方法,也就是說不會有默認構造函數
class person: NSObject { var name : String // 自定義構造函數,覆蓋init:函數 init(name : String) { // 在初始化self.name屬性時沒有給它賦值,所以可以在構造函數里面進行賦值 self.name = name } } // 創建person對象 let ps : person = person(name: "laoWang") print(ps.name) // 結果:laoWang
-
字典轉模型方式一
- 開發中,我們經常會將字典轉換成模型在來使用,這邊就以此做例子
- 需要注意的是:字典中取出的數據類型為NSObject,我們可以通過as!將其轉成需要的類型
class person: NSObject { var name : String // 自定義構造函數,覆蓋init:函數 init(dict : [String : NSObject]) { self.name = dict["name"] as! String } } // 創建person對象 let ps : person = person(dict:["name" : "laoWang"]) print(ps.name) // 結果:laoWang
-
字典轉模型方式二
開發中我們經常會用KVC的方式將字典轉成模型,這邊就使用KVC對字典進行轉換
-
需要注意的是:KVC不能保證給所有屬性賦值,所以屬性需要有默認的值
- 對象、結構體類型定義一般為可選類型就可以了,因為可選類型沒有賦值前為nil
- 基本數據類型一般設置為0
class person: NSObject { // KVC方式下,對象、結構體類型必須是可選類型,否則無法轉換 var name : String? // 自定義構造函數,覆蓋init:函數 init(dict : [String : NSObject]) { // 必須先初始化對象 super.init() // 調用對象的KVC方法 setValuesForKeysWithDictionary(dict) } } // 創建person對象 let ps : person = person(dict:["name" : "laoWang"]) print(ps.name) // 結果:laoWang