Swift003-訪問修飾詞 函數(shù) 閉包
訪問限制詞
在 Swift 語言中,訪問修飾符有五種,分別為 fileprivate,private,internal,public 和 open。
權(quán)限從高到底 open > public > interal > fileprivate > private
- private 訪問級別所修飾的屬性或者方法只能在當前類里訪問(swift4開始 extension 里也可以訪問 private 的屬性)。
- fileprivate 訪問級別所修飾的屬性或者方法在當前的 Swift 源文件里可以訪問。
- internal 訪問級別所修飾的屬性或方法在源代碼所在的整個模塊都可以訪問。
- internal 訪問級別所修飾的屬性或方法在源代碼所在的整個模塊都可以訪問。
- 如果是框架或者庫代碼,則在整個框架內(nèi)部都可以訪問,框架由外部代碼所引用時,則不可以訪問。
- 如果是 App 代碼,也是在整個 App 代碼,也是在整個 App 內(nèi)部可以訪問。
- 默認訪問級別,internal修飾符可寫可不寫
- public 可以被任何人訪問。但其他 module 中不可以被重寫(override)和繼承,而在 module 內(nèi)可以。
- open 可以被任何人使用,包括 override 和繼承
函數(shù)
- 一個完整的函數(shù)由 訪問修飾符+類型+func+方法名+參數(shù)標簽+參數(shù)名稱+返回值 組成
- 面向?qū)ο笊衔覀兞晳T稱 方法
// 定義
// 多個參數(shù)可以(但最好不要)有相同的參數(shù)標簽,不可以有相同的參數(shù)名稱
// "_"下劃線用于隱藏標簽
// 參數(shù)可以寫進固定值
// 返回值有多個時可以利用元組,也可以用數(shù)組,字典的形式
public class func test(_ fruit: String, priceLabel price: Float, count: Int, shop: String = "超市") -> (String, Float, Int) {
return (fruit, price, count)
}
// 使用
test2("pear", priceLabel: 1.25, count: 1)
- 類方法
public class func test1() {
print("類方法")
}
- 靜態(tài)方法
public class func test2() {
print("靜態(tài)方法")
}
- 實例方法(對象方法)
func test3() {
print("實例方法")
}
- 無參數(shù)無返回值(Void而已,省略)
func test4() {
print("無參數(shù)無返回值")
}
- 可變形參(可變參數(shù):參數(shù)數(shù)量不定)
func test5(nums: Int...) {
let sum = nums.reduce(0) { (result, num) -> Int in
return result + num
}
print("\(nums) 的和: \(sum)")
}
test5(nums: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
- 輸入輸出參數(shù)
默認情況下,函數(shù)參數(shù)是常量。嘗試從該函數(shù)體內(nèi)更改函數(shù)參數(shù)的值會導致編譯時錯誤。這意味著無法更改參數(shù)的值。
通過將inout關(guān)鍵字放在參數(shù)類型之前來編寫輸入輸出參數(shù)。一個輸入輸出參數(shù)傳遞進函數(shù)的值,可以由函數(shù)修改并替換原來的值。
將變量作為輸入輸出參數(shù)的參數(shù)傳遞。不能傳遞常量或文字值作為參數(shù),因為不能修改常量和文字。當使用輸入輸出參數(shù)調(diào)用函數(shù)時在變量的名稱前直接放置一個&,以指示該函數(shù)可以修改它。
輸入輸出參數(shù)不能具有默認值,并且可變參數(shù)不能標記為inout。
func inoutFunc(argument1: inout Int, argument2: inout Int) {
let temp = argument1
argument1 = argument2
argument2 = temp
}
var inout1 = 1
var inout2 = 2
inoutFunc(argument1: &inout1, argument2: &inout2)
print("inout1: \(inout1), inout2: \(inout2)")
函數(shù)類型
每個函數(shù)都有一個特定的函數(shù)類型,由函數(shù)的參數(shù)類型和返回類型組成。形式上表示為:(參數(shù)類型) -> 返回值類型。
- 函數(shù)類型作為參數(shù)類型
可以使用函數(shù)類型,作為另一個函數(shù)的參數(shù)類型。這使得可以為函數(shù)調(diào)用者保留函數(shù)實現(xiàn)的某些方面,以便在調(diào)用函數(shù)時提供:
func testAdd(a:Int, b:Int) -> Int {
return a + b
}
func testFuncParam(_ addFunction: (Int, Int) -> Int, a:Int, b:Int) {
print("Result:\(addFunction(a,b))")
}
func testBasic() {
testFuncParam(testAdd, a: 5 ,b: 7)
}
- 函數(shù)類型作為返回類型
可以使用函數(shù)類型作為另一個函數(shù)的返回類型。通過在返回函數(shù)的返回箭頭(->)之后立即編寫完整的函數(shù)類型來完成此操作:
// 定義個自增函數(shù)
func testIncrease(input:Int) -> Int {
return input + 1
}
// 定義個自減函數(shù)
func testReduce(input:Int) -> Int {
return input - 1
}
// 定義一個返回函數(shù)類型的函數(shù)
func testFunctionBack(backwards:Bool) -> (Int) -> Int {
return backwards ? testReduce : testIncrease
}
- 嵌套函數(shù)
在函數(shù)體內(nèi)定義的函數(shù)稱為嵌套函數(shù),嵌套函數(shù)對外界是隱藏的,但它們的封閉函數(shù)仍然可以調(diào)用它們。封閉函數(shù)也可以返回其嵌套函數(shù),以允許嵌套函數(shù)在另一個范圍內(nèi)使用:
func sumFunc() -> (Int...) -> Int {
func sumInts(nums: Int...) -> Int {
return nums.reduce(0) { (result, item) -> Int in
result + item
}
}
return sumInts(nums:)
}
print(sumFunc()(1, 2, 3, 4, 5))
- 構(gòu)造函數(shù)和析構(gòu)函數(shù)
// 屬性
var name: String
// 構(gòu)造函數(shù)(初始化)
init(newName:String){
self.name = newName
print("My name is \(newName)")
}
// 析構(gòu)函數(shù)(反初始化,功能跟oc的dealloc一樣做善后)
deinit {
name = ""
}
-
class與staitc關(guān)鍵字的區(qū)別與使用
- static 可以在類、結(jié)構(gòu)體、枚舉、擴展中使用。而 class 只能在類中使用。
- static 可以修飾存儲屬性,static 修飾的存儲屬性稱為靜態(tài)變量(常量)。而 class 不能修飾存儲屬性。
- static 修飾的計算屬性不能被重寫。而 class 修飾的可以被重寫。
- static 修飾的靜態(tài)方法不能被重寫。而 class 修飾的類方法可以被重寫。
- class 修飾的計算屬性被重寫時,可以使用 static 讓其變?yōu)殪o態(tài)屬性。
- class 修飾的類方法被重寫時,可以使用 static 讓方法變?yōu)殪o態(tài)方法。
閉包
閉包是swift中非常重要的一個知識點,類似于objective-c中的block。
閉包的本質(zhì)是代碼塊,是函數(shù)的升級版,函數(shù)是有名稱、可復用的代碼塊,閉包則是比函數(shù)更加靈活的匿名代碼塊。
(函數(shù)相當于一個特殊的閉包(有名字的閉包),或者說閉包是一個特殊的函數(shù)(帶有自動變量的匿名函數(shù)))
由上面括號內(nèi)語句, 可以歸納三種閉包形式:
- 全局函數(shù):具名函數(shù),但不捕獲任何值
- 嵌套函數(shù):在函數(shù)內(nèi)部嵌套定義具名函數(shù),可捕獲包含函數(shù)中的值
- 閉包表達式:匿名函數(shù)類型實例,不具名的代碼塊,輕量級語法,可捕獲上下文的值
其中閉包表達式才是我們一般意義上說的閉包
Swift的閉包表達式具有干凈,清晰的風格,閉包的優(yōu)勢包括:
- 從上下文中推斷參數(shù)和返回值類型
- 單表達式閉包的隱式返回
- 速記參數(shù)名稱
- 尾隨閉包語法
閉包定義的類型
{ (參數(shù)1,參數(shù)2... )->返回值類型 in
語句塊
}
閉包表達式以及簡化形式:
import UIKit
/// 起別名
typealias DDYTestClosureTypealias = (String, Bool) -> Void
class ClosureTest: NSObject {
public class func testBasic() {
testTypealias { (city: String, success: Bool) in
print("\(city) \(success)") // Beijing true
}
testParams()
}
private class func testTypealias(_ closure: DDYTestClosureTypealias) {
closure("Beijing", true);
}
private class func testParams() {
let numbersArray = [1, 3, 5, 9, 7, 2, 8, 6, 0]
// 參數(shù)加類型
let sorted1 = numbersArray.sorted { (num1: Int, num2: Int) -> Bool in
return num1 > num2 // 降序
}
// 參數(shù)省略類型(編譯器推斷類型)
let sorted2 = numbersArray.sorted { num1, num2 in
return num1 < num2 // 升序
}
// 省略參數(shù)名和類型,用$加數(shù)字引用每個參數(shù)
let sorted3 = numbersArray.sorted {
return $1 > $0
}
// 如果閉包只包含一行代碼,省略return都可以
let sorted4 = numbersArray.sorted {
$0 > $1
}
// 閉包還可以存儲在變量中,類似函數(shù)一樣調(diào)用
let comparator = {(a: Int, b: Int) in a<b }
let result = comparator(1, 2)
print("\(sorted1)")
print("\(sorted2)")
print("\(sorted3)")
print("\(sorted4)")
print("\(result)")
// [9, 8, 7, 6, 5, 3, 2, 1, 0]
// [0, 1, 2, 3, 5, 6, 7, 8, 9]
// [0, 1, 2, 3, 5, 6, 7, 8, 9]
// [9, 8, 7, 6, 5, 3, 2, 1, 0]
// true
}
}
將操作符函數(shù)自動推導為函數(shù)類型
- 尾隨閉包:當閉包表達式為函數(shù)最后一個參數(shù),可將其寫在括號后。
尾隨閉包用于需要將一個很長的閉包表達式作為最后一個參數(shù)傳遞給函數(shù)。也就是說如果按時的最后一個參數(shù)是閉包,那么在調(diào)用它的時候就可以把這個閉包寫在括號外面,并緊跟括號,函數(shù)的其他參數(shù)則仍然寫在括號之中
private class func testCloseClosure(paramStr: String, paramClosure: (String) -> Void) {
paramClosure("輸出"+paramStr)
}
private class func testCloseClosurePrint() {
// 普通調(diào)用
testCloseClosure(paramStr: "1 普通調(diào)用", paramClosure: { (closureStr) in
print(closureStr)
})
// 尾隨閉包
testCloseClosure(paramStr: "2 尾隨閉包") { (closureStr) in
print(closureStr)
}
// 輸出1 普通調(diào)用
// 輸出2 尾隨閉包
}
- 自動閉包:不接受任何參數(shù),直接返回表達式的值。允許延遲計算。
var cities = ["Beijing","Shanghai","New York", "Paris","London"]
print(cities.count) //此時的城市數(shù)是5
let filter = { cities.removeLast() } //()->String 將閉包賦值給filter,此時還沒有執(zhí)行removeLast()
print(cities.count) //由于上一句并沒有執(zhí)行removeLast()此時的城市數(shù)還是5
print("Deleting \(filter())!") //執(zhí)行了filter的閉包。 顯示Deleting London!
print(cities.count) //此時的城市數(shù)是4, London被刪除掉了
函數(shù)類型與閉包的變量捕獲
函數(shù)類型和閉包可以捕獲其所在上下文的任何值:
- 函數(shù)參數(shù)
- 局部變量
- 對象實例屬性
- 全局變量
- 類的類型屬性
//捕獲實例屬性
class Rectangle{
var width = 0
var length = 0
func getComputHandler()-> ()->Int {
return {
return self.width*self.length //這個閉包就捕獲了實例屬性self.width和self.length
}
}
}
//捕獲參數(shù)或局部變量
func addHandler(step: Int)-> ()->Int{
var sum = 0
return{
sum +=step //這里捕獲了參數(shù)step和局部變量sum
return sum
}
}
let addByTen = addHandler(10)
print(addByTen()) //顯示結(jié)果 10
print(addByTen()) //顯示結(jié)果 20
print(addByTen()) //顯示結(jié)果 30
如果捕獲值生存周期小于閉包對象(參數(shù)和局部變量),系統(tǒng)會將被捕獲的值封裝在一個臨時對象里,然后再閉包對象上創(chuàng)建一個對象指針,指向該臨時對象。
臨時對象和閉包對象之間是強引用關(guān)系,生存周期跟隨閉包對象。
起別名
// 光強檢測閉包起別名
typealias DDYQRBrightnessClosure = (_ brightnessValue: Double) -> Void
// 光強檢測數(shù)據(jù)閉包回調(diào)
public var brightnessClosure: DDYQRBrightnessClosure?
使用閉包需要注意內(nèi)存管理,當閉包為參數(shù)時如果以脫離函數(shù)則需要聲明為逃逸閉包(類型前加@escaping)
逃逸閉包
當一個閉包作為參數(shù)傳到一個函數(shù)中,但是該閉包要在函數(shù)返回之后才被執(zhí)行,于是就稱這樣的閉包為逃逸閉包。也就是說閉包逃離了函數(shù)的作用域。寫法是在這個閉包參數(shù)前加一個@escaping用來指明這個閉包是允許逃逸出該函數(shù)的。
//定義數(shù)組,里面的元素都是閉包類型的
var callBackArray : [(Int)->Void] = []
//定義一個接收閉包的函數(shù)
private func testEscapingClosure() {
testEscapingClosureChange()
print("111: \(closureX)")
callBackArray.first?(5)
print("222: \(closureX)")
let closure = callBackArray.first
closure?(6)
print("333: \(closureX)")
}
var closureX = 10
private func testEscapingClosureChange() {
testEscapingClosureCallBack { (closureParam) in
self.closureX = self.closureX+closureParam
}
}
private func testEscapingClosureCallBack(callBack:@escaping (Int)-> Void) {
callBackArray.append(callBack)
}
逃逸閉包實際應用
/// 相機權(quán)限
///
/// - Parameter closure: 閉包回調(diào)
public static func cameraAuth(_ closure: @escaping (Bool) -> Void) -> Void {
let authStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
switch authStatus {
case .notDetermined:
AVCaptureDevice.requestAccess(for: AVMediaType.video) { (granted: Bool) in
DispatchQueue.main.async {
closure(granted)
}
}
break
case .authorized:
DispatchQueue.main.async {
closure(true)
}
break
default: // case .restricted // case .denied
DispatchQueue.main.async {
closure(false)
}
break
}
}
// 1、可以使用weak關(guān)鍵字將對象之間的聯(lián)系變?yōu)槿跻?weak var weakself = self
// 2、第一種方式的簡化
[weak self]
// 3、使用unowned解決
[unowned self]
// 但是該方法十分危險,要確保數(shù)據(jù)一定有值,否則會Crash