swift-05-函數、閉包

最近在學習swift,期間也看了不少教程和大神的博客,最後把筆記放這裏,方便查看複習~swift

附上學習資料地址:w3cschool-Swift 教程YungFan大神的簡書(推薦看一下,有些知識點總結地簡直通俗易懂)api

1、函數

一、介紹

函數就是oc中的方法數組

常規格式:markdown

func 函數名(參數列表) -> 返回值類型 {
    代碼塊
    return 返回值
}
複製代碼

二、多種狀況下的格式

一、無參+無返回值閉包

//常規寫法
func about() -> Void {
    print("iPhone Xs Max")
}
// 調用函數
about()

// 簡單寫法
// 若是沒有返回值,Void能夠寫成()
func about1() -> () {
    print("iPhone Xs Max")
}
about1()

// 若是沒有返回值,後面的內容能夠都不寫
func about2() {
    print("iPhone Xs Max")
}

about2()
複製代碼

二、無參+有返回值app

func readMessage() -> String {
    return "吃飯了嗎?"
}
var str = readMessage()
print(str)
複製代碼

三、有參+無返回值異步

func call(phoneNum : String) {
    print("打電話給\(phoneNum)")
}
call("18888888888")
複製代碼

四、有參+有返回值函數

func sum(num1 : Int, num2 : Int) -> Int {
    return num1 + num2
}
var result = sum(num1: 20, num2: 30)
print(result)
複製代碼

五、返回值爲複雜類型oop

//大體意思爲,返回值是一個元組類型
func getInfo(info:String) -> (name:String, age:Int) {

    let infos = info.components(separatedBy: ",")

    //注意,第二個類型要轉換一下,而且加!
    return (infos[0], Int(infos[1])!)

}

let person:(name:String, age:Int) = getInfo(info: "zhangsan,20")

print(person.name)
print(person.age)
複製代碼

三、注意事項

一、函數的參數是常量;學習

二、形參標籤+形參名稱,也就是:外部參數名+內部參數名+類型;

函數參數的常規寫法

func addFunc(num1 a: Int,num2 b: Int) -> Int {
    return a + b
}

let result = addFunc(num1: 5, num2: 5)

print(result) //10
複製代碼

簡寫,省略

func addFunc(_ a: Int,_ b: Int) -> Int {
    return a + b
}

let result = addFunc(5, 5)

print(result) //10
複製代碼

三、能夠設置默認參數(在沒有傳入參數的狀況下);

func makecoffee(type :String = "冰美式") -> String {
    return "來一杯\(type)。"
}

let coffee1 = makecoffee(type: "拿鐵")
print(coffee1) //來一杯拿鐵。

let coffee2 = makecoffee()
print(coffee2) //來一杯冰美式。
複製代碼

四、可變參數:swift中接受參數的個數不肯定,但類型要相同;

func total(numbers:Int...) -> Int {
    var sum = 0
    for i in numbers {
        sum += i
    }
    return sum
}

let res1 = total()
let res2 = total(numbers:10)
let res3 = total(numbers:10,20)
let res4 = total(numbers:10,20,30)

print(res1) //0
print(res2) //10
print(res3) //30
print(res4) //60
複製代碼

五、inout引用類型:通常參數只是傳遞一個值進來,若是想改變外部參數就要用到inout,相似block中的__block;

func changeInt(a: inout Int, b: inout Int) {

    a += 12
    b += 100
}

var a = 10
var b = 20

print("調用前:a=\(a), b=\(b)") 
//打印:調用前:a=10, b=20

changeInt(a: &a, b: &b)

print("調用後:a=\(a), b=\(b)") 
//打印:調用後:a=22, b=120
複製代碼

六、函數的嵌套:內嵌的函數只能在該函數裏面調用

四、函數的類型及嵌套函數

一、函數的引用類型 二、每一個函數都有本身的類型 三、定義+使用->代碼示例 四、嵌套函數(一個函數做爲另外一個函數的參數 、一個函數做爲另外一個函數的返回值)

函數類型的使用:

//定義一個函數
func addTwoInts(a : Int, b : Int) -> Int {
    return a + b
}

//這個函數的類型是:(Int, Int) -> Int
//因而,咱們能夠這麼使用:
var mathFunction : (Int, Int) -> Int = addTwoInts

//而後能夠這麼調用
let res = mathFunction(10, 20)
print(res)

複製代碼

一個函數做爲另外一個函數的參數:

//定義一個函數
func addTwoInts(a : Int, b : Int) -> Int {
    return a + b
}

//這個函數的類型是:(Int, Int) -> Int
//做爲一個參數傳入另外一個函數
//參數名: 函數類型

// 將函數的類型做爲函數的參數
func printResult(a : Int, b : Int, calculateMethod : (Int, Int) -> Int) {
    print(calculateMethod(a, b))
}

//調用自定義函數
printResult(a: 10, b: 20, calculateMethod: addTwoInts)

//也能夠調用 手寫一個閉包穿進去
printResult(a: 12, b: 22, calculateMethod: {(m: Int, n: Int) -> Int in return m + n})
複製代碼

一個函數做爲另外一個函數的返回值:

//定義兩個函數
func addTwoInts(a : Int, b : Int) -> Int {
    return a + b
}

func multiplyTwoInt(a : Int, b : Int) -> Int {
    return a * b
}

//函數做爲返回值
func getResult(a:Int) -> (Int, Int)->Int{
    if a > 10 {
        return addTwoInts
    }
    else{
        return multiplyTwoInt
    }
}
//調用返回的函數
let res1 = getResult(a: 2)(10,20)
print(res1) //200

let res2 = getResult(a: 12)(10,20)
print(res2) //30
複製代碼

2、閉包

一、介紹

閉包:是一個能夠被傳遞、引用的獨立模塊;

它可以捕捉、存儲 定義在它上下文中的任何常量和變量,也就是說:閉合幷包裹 其裏面的常量和變量,所以叫作:閉包

閉包和函數同樣,都是引用類型

分爲三種形式:

  • 全局函數,是一個有名字但不會捕捉任何值的閉包
  • 內嵌函數,是一個有名字且能夠從上層函數捕捉值的閉包
  • 閉包表達式,是一個輕量級語法,能夠捕捉其上下文中的常量和變量值的沒有名字的閉包

二、與函數比較

計算一個數的特定值,好比平方

函數的寫法

func square(num: Int)  -> Int{
    return num * num
}

//調用
square(num: 5)
複製代碼

閉包的寫法

let squareCloure = { (num: Int) -> Int in
    return num * num
}
//調用
squareCloure(6)
複製代碼
實戰場景

條件

//這是一個函數,它裏面有一個參數 須要傳入一個函數/閉包
//從數組中篩選指出合適的數據組成新的數組
func getList(score:[Int], con:(Int)->Bool) -> [Int]{

    var newScore:[Int] = [Int]()
    for item in score {
        //把每個元素傳入con方法,根據返回值判斷是否執行{}
        if con(item) {
            newScore.append(item)
        }
    }
    return newScore
}

//已知數組
let scoreArray = [75,60,95,45,85]
複製代碼

函數方式處理

//函數的方式傳參,先聲明一個函數
func method(num: Int) -> Bool{
    return num > 50
}

let newArrayM = getList(score: scoreArray, con: method)
print(newArrayM) //[75, 60, 95, 85]
複製代碼

閉包方式處理

//閉包方式傳參,寫法1
let closure = {(num: Int) -> Bool in
    return num > 50
}

let newArrayC = getList(score: scoreArray, con: closure)
print(newArrayC) //[75, 60, 95, 85]




//閉包方式傳參,寫法2
let newArrayC2 = getList(score: scoreArray,  con:{(num: Int) -> Bool in return num > 50})

print(newArrayC2) //[75, 60, 95, 85]
複製代碼

三、主要知識點

一、由in關鍵字,將閉包分紅兩個部分:參數與返回值、閉包體 二、與函數不一樣的一點:參數不能設置默認值,其餘與函數同樣 三、參數名稱有縮寫功能,經過$0、$一、$2依次進行參數的調用 四、能夠根據不一樣狀況進行簡寫:

  • 一、省略 ->返回值類型 (根據後面表達式能夠推斷返回值是一個Bool)
let newArrayC2 = getList(score: scoreArray,  con:{(num: Int) in return num > 50})
複製代碼
  • 二、省略 參數類型括號 (根據函數的參數可推斷傳進來的必然是Int)
let newArrayC2 = getList(score: scoreArray,  con:{num in return num > 50})
複製代碼
  • 三、省略return (由於是單行語句,能夠省略return)
let newArrayC2 = getList(score: scoreArray,  con:{num in num > 50})
複製代碼
  • 四、經過參數名稱縮寫,省略 參數聲明in
let newArrayC2 = getList(score: scoreArray,  con:{$0 > 50})
複製代碼

四、捕獲

  • 閉包能夠從上下文環境中捕獲常量、變量,並在本身的做用域內使用

  • Swift最簡單的閉包形式是嵌套函數,也就是定義在其餘函數的函數體內的函數,嵌套函數能夠捕獲其外部函數全部的參數以及定義的常量和變量

//這裏是一個嵌套函數,也能夠當作 返回值是一個閉包
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
    	//在閉包裏能夠捕獲到本方法外的變量
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let incrementByTen = makeIncrementor(forIncrement: 10)

print(incrementByTen())// 返回的值爲10
複製代碼

五、尾隨閉包

按我的理解就是爲了增長可讀性,閉包的寫法改了個格式

條件:有一個函數,閉包是這個函數的最後一個參數 作法:省略閉包的形參,並把小括號提早,最後使用{}進行閉包的傳遞 好處:增長可讀性

示例:

//函數,同時 閉包是最後一個參數
func doSomething(info:String, clousre:(String)->Void){
     clousre(info)
}

//正常調用
doSomething(info: "Hello", clousre: { str in print(str) })

//使用尾隨閉包進行函數調用
doSomething(info: "World") { str in
    print(str)
}
        
        
複製代碼

六、逃逸閉包

使用@escaping 放在閉包形參類型以前,代表這是一個逃逸閉包,這個閉包會在 函數結束以後被調用。

用途:逃逸閉包經常使用於異步回調

//聲明一個變量用於測試
var x = 10

//聲明一個存放閉包的數組
var closureArray :[()->Void] = [()->Void]()


//爲了對比,先定義一個函數,參數是一個正常閉包
func normolClosure(closure:()->Void){
    closure()
}
//再定義一個函數,參數是一個逃逸閉包
//把閉包 存儲到 閉包數組中,但沒有調用。等最後調用數組中的閉包
func escapeClosure(closure: @escaping ()->Void){
    print("函數開始")
    closureArray.append(closure)
    print("函數結束")
}


print("調用函數前,檢測 x = \(x)")

/*
 調用正常閉包函數
 因爲閉包是最後一個參數,能夠增長可讀性,使用尾隨閉包
 */
normolClosure {
    x = 100
}
//打印100 由於閉包在函數裏面執行了
print("調用正常閉包函數後,檢測 x = \(x)")

//調用逃逸閉包函數
escapeClosure {
    x = 200
}

//此時仍是100,由於沒有調用 逃逸閉包
print("調用逃逸閉包函數後,檢測 x = \(x)")


//在函數外面調用逃逸閉包
closureArray.first?()

print("函數外面調用逃逸閉包,檢測 x = \(x)")



/*
 打印:
 調用函數前,檢測 x = 10
 調用正常閉包函數後,檢測 x = 100
 函數開始
 函數結束
 調用逃逸閉包函數後,檢測 x = 100
 函數外面調用逃逸閉包,檢測 x = 200
 */
複製代碼

七、自動閉包

使用@autoclosure 放在閉包形參類型以前,代表這是一個自動閉包。

用於包裝函數參數的表達式,不接受任何參數,被調用時會返回被包裝在其中的表達式的值

//自動閉包
func printIfTrue(predicate:@autoclosure ()->Bool){  
    if predicate() {
        print("is true")   
    }    
    else{       
        print("is false")       
    }   
}

//直接進行調用了,Swift 將會把 2 > 1 這個表達式自動轉換爲 () -> Bool。這樣咱們就獲得了一個寫法簡單、表意清楚的表達式。
printIfTrue(predicate: 2>1)
printIfTrue(predicate: 2<1)
複製代碼

八、解決循環引用

方案一:weak

  • 經過weak修飾self對當前類進行弱引用

  • self屬於可能有值也可能沒有值的狀況,所以weakSelf是一個可選類型

  • 又由於當前self必定存在,不然根本都不在這個方法裏,因此在調用的時候可使用強制解包(!),

weak var weakSelf = self

someView.block = {
    //執行當前類的某個方法
    weakSelf!.refreshView()
}
複製代碼

方案二:也是weak,屬於方案一的簡寫

someView.block = { [weak self] in
    //執行當前類的某個方法
    self?.refreshView()
}
複製代碼

方案三:unowned

  • unowned有點相似oc中的unsafe_unretained

  • 它表示:即便它本來引用的對象被釋放了,扔會保持着對這個已經被釋放的對象有一個無效的引用,所以它不能是可選值,也不會指爲nil

someView.block = { [unowned self] in
    //執行當前類的某個方法
    self.refreshView()
}
複製代碼
相關文章
相關標籤/搜索