swift-函数

要了解swift的函数和闭包,首先要了解三件事

  1. 函数可以像String 和 Int 那样可以赋值给变量,也可以作为另一个函数的参数和返回值。(函数是头等对象,这一点最重要)
  2. 函数能捕获存在于其作用域之外的变量。
  3. 有两种方式可以创建函数。一种是使用func关键字,另一种是 {}。在swift中后一种被称为闭包。
var str = "Hello, playground"
/// MARK: ---------------------变量
func printInt(i: Int) {
    print("you passed \(i)")
}

// 函数赋值 不加括号
let funVar = printInt

funVar(1)

// 一个接收函数作为参数的函数

func useFunction(function: (Int) -> Void) {
    function(2)
}

useFunction(function: printInt)

useFunction(function: funVar)

// 也可以返回一个 函数

func returnFunc() -> (Int) -> String {
    func innerFunc(i: Int) -> String {
        return "you passed \(i)"
    }
    return innerFunc
}

let myFunc = returnFunc()
myFunc(3)

/// MARK: ---------------------值捕获
// 当函数应用了函数作用域外部的变量时, 这个变量就被捕获了,它将在超出函数作用域后继续存在,而不是结束。

func counterFunc() -> (Int) -> String {
    var counter = 0
    func innerFunc(i: Int) -> String {
        counter += i
        return "running total: \(counter)"
    }
    return innerFunc
}
// counter 将会保存在堆上
let f = counterFunc()
f(3)
f(4)
// 创建新的函数会生成新的counter 不会影响f
let g = counterFunc()

g(2)
g(3)
// 在 编程中 一个函数和它捕获的环境变量组合起来称之为闭包

/// MARK: ---------------------闭包
// 函数可以用{}来声明闭包表达式 使用闭包来定义函数可以称之为函数的字面量 类似于 1 就是整数的字面量, “hello” 是字符串的字面量。
// 闭包与函数区别在于 它没有名字 只能直接赋值给一个变量
// 但是它和函数完全等价 甚至拥有同一个 命名空间
func doubler(i: Int) -> Int {
    return i*2
}

let doubilerAlt = { (i: Int) -> Int in return i*2 }

[1, 2, 3, 4].map(doubilerAlt)

// 闭包可以简写

[1, 2, 3, 4].map{ $0*2 }

// 闭包可以不声明变量类型 也可以事先指定 闭包使用时一般已经有上下文所以第二种写法不重要


let isEven = {$0 % 2 == 0} ///会默认推定为 (Int) -> Bool

let isEvenAlt = {(i: Int) -> Bool in
    return i % 2 == 0
}

/// 总结:闭包是函数和捕获变量的组合,{}创建的函数是闭包表达试 我们称之为闭包但其实它与函数并无区别,它们都可以是函数也都可以是闭包

函数的灵活性

// 实现排序
let myArray = [3, 1, 2]

myArray.sorted(by: >)

/// 或者提供一个复杂的函数 来任意规则排序

let annimals = ["elephant", "zebra", "dog"]

annimals.sorted { v1, v2 in
    let l = v1.reversed()
    let r = v2.reversed()
    return l.lexicographicallyPrecedes(r)
}

/// 使用OC 写一个复杂的排序

final class Person: NSObject {
    @objc var first: String
    @objc var last: String
    @objc var yearOfBrith: Int

    init(first: String, last: String, year: Int) {
        self.first = first
        self.last = last
        self.yearOfBrith = year
    }
}

let people = [
    Person(first: "jo", last: "smith", year: 1970),
    Person(first: "joe", last: "smith", year: 1970),
    Person(first: "joe", last: "smyth", year: 1970),
    Person(first: "joanne", last: "smith", year: 1985),
    Person(first: "joanne", last: "smith", year: 1970),
    Person(first: "robet", last: "smith", year: 1970),
]

/// OC 中 类型不安全 依赖运行时的方式
let lastDesciptor = NSSortDescriptor(key: #keyPath(Person.last), ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
let firstDesciptor = NSSortDescriptor(key: #keyPath(Person.first), ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
let yearDesciptor = NSSortDescriptor(key: #keyPath(Person.yearOfBrith), ascending: true)

let descriptors = [lastDesciptor, firstDesciptor, yearDesciptor]

(people as NSArray).sortedArray(using: descriptors)


/// 使用swift函数 不能实现 year排序  (lexicographicallyPrecedes 顺序执行 成功一个就返回)

people.sorted { (p0, p1) -> Bool in
    let left = [p0.last, p0.first]
    let right = [p1.last, p1.first]
    return left.lexicographicallyPrecedes(right, by: { (v1, v2) -> Bool in
        return v1.localizedCaseInsensitiveCompare(v2) == .orderedAscending
    })
}
/// MARK: ---------------------函数作为数据 用函数来描述对象的数据
typealias SortDescriptor = (Value, Value) -> Bool

let sortByYear: SortDescriptor = { $0.yearOfBrith < $1.yearOfBrith }
let sortByLastName: SortDescriptor = { $0.last.localizedCaseInsensitiveCompare($1.last) == .orderedAscending }
let sortByFirstName: SortDescriptor = { $0.first.localizedCaseInsensitiveCompare($1.first) == .orderedAscending }

/// 封装一个 和 NSSortDescriptor 类似的函数 但不涉及运行时 接受一个键 和一个 比较方法 返回一个排序描述 SortDescriptor 函数
/// 在这里key 也是一个 函数 (用于取值)

func sortDescriptor(key: @escaping (Value) -> Key,
                                _ areInIncreaingOrder: @escaping (Key, Key) -> Bool) -> SortDescriptor {
    return { areInIncreaingOrder(key($0), key($1)) }
}

let sortByYearAt: SortDescriptor = sortDescriptor(key: {$0.yearOfBrith}, <)
people.sorted(by: sortByYearAt)

/// 为Compareable 重载一个函数

func sortDescriptor(key: @escaping (Value) -> Key) -> SortDescriptor where Key: Comparable {
    return { key($0) < key($1) }
}

/// 上面的 sortDescriptor 返回都是 Bool 函数 NSSortDescriptor 有一种返回 升序 降序 相等的初始化
func sortDescriptor(key: @escaping (Value) -> Key,
                                ascending: Bool = true,
                                _ comparetor: @escaping (Key)
    -> (Key) -> ComparisonResult)
    -> SortDescriptor {
    return { lhs, rhs in
        let order: ComparisonResult = ascending ? .orderedAscending : .orderedDescending
        return comparetor(key(lhs))(key(rhs)) == order
    }
}
/// 这样 我们就实现了 一个 NSSortDescriptor 相同能力的函数 但是它具有类型安全 不依赖运行时
let sortByFirstNameAt: SortDescriptor = sortDescriptor(key: {$0.first}, String.localizedCaseInsensitiveCompare)
people.sorted(by: sortByFirstNameAt)

/// 再写一个 工作方式 类似于 sortedArra(using:) 的功能的函数
func combine (sortDescriptors: [SortDescriptor]) -> SortDescriptor {
    return { lhs, rhs in
        for areInIncreasingOrder in sortDescriptors {
            if areInIncreasingOrder(lhs, rhs) {
                return true
            }
            if areInIncreasingOrder(rhs, lhs) {
                return false
            }
        }
        return false
    }
}
/// 这样我们就可以重写OC的方法了

let combind: SortDescriptor = combine(sortDescriptors: [sortByLastName, sortByFirstName, sortByYear])

people.sorted(by: combind)
/// swift作为编译时就确定了静态类型的语言 也能 实现部分动态行为
/// combine 展示了这种合并函数的函数的用法

/// MARK: --------------------- 定义一个合并函数的运算符

infix operator <||> : LogicalDisjunctionPrecedence

func <||>(lhs: @escaping(A, A) -> Bool, rhs: @escaping(A, A) -> Bool) -> (A, A) -> Bool {
    return { x, y in
        if lhs(x, y) {
            return true
        }
        if lhs(y, x) {
            return false
        }
        if rhs(x, y) {
            return true
        }
        return false
    }
}

/// 再次优化

let combindAt = sortByLastName <||> sortByFirstName <||> sortByYear

people.sorted(by: combindAt)

/// 实现 localizedCaseInsensitiveCompare 应用到可先值

func lift(_ compare: @escaping (A) -> (A) -> ComparisonResult)
    -> (A?) -> (A?) -> ComparisonResult {
        return { lhs in { rhs in
            switch (lhs, rhs) {
            case (nil, nil):
                return .orderedSame
            case (nil, _):
                return .orderedAscending
            case (_, nil):
                return .orderedDescending
            case let (l?, r?):
                return compare(l)(r)
            default:
                 fatalError()
            }
        }
    }
}

let lcic = lift(String.localizedCaseInsensitiveCompare)

/// swift 使用内省排序 不能保证相同值的顺序 是不稳定的

局部函数和变量捕获

  • 实现一个 稳定排序归并排序,首先拆分待排序的数组,然后进入合并merge,我们将merge单独定义为一个函数,但是merge需要一个临时的存储空间。
extension Array where Element: Comparable {
    private mutating func merge(lo: Int, mi: Int, hi: Int) {
        print("lo:\(lo) mi:\(mi) hi:\(hi)")
        var temp: [Element] = []
        var i = lo, j = mi
        while i != mi && j != hi {
            if self[j] < self[i] {
                temp.append(self[j])
                j += 1
            } else {
                temp.append(self[i])
                i += 1
            }
            print(temp)
        }
        print(temp)
        temp.append(contentsOf: self[i..

函数作为代理

  • 如果代理只是一个简单的方法那么可以使用函数来取代
var str = "Hello, playground"
/// MARK: ---------------------归并排序

//protocol AlertViewDelegate: class {
//    func buutonTapped(index: Int)
//}

//class AlertView {
//    var buttons: [String]
//    weak var delegate: AlertViewDelegate?
//    init(buttons: [String] = ["ok", "cancel"]) {
//        self.buttons = buttons
//    }
//
//    func fire() {
//        delegate?.buutonTapped(index: 1)
//    }
//}
// 有时候 我们希望一个结构体实现代理 那么 weak 就不能用了 就有可能内存泄漏
//protocol AlertViewDelegate {
//    mutating func buutonTapped(index: Int)
//}
//
//class AlertView {
//    var buttons: [String]
//    var delegate: AlertViewDelegate?
//    init(buttons: [String] = ["ok", "cancel"]) {
//        self.buttons = buttons
//    }
//
//    func fire() {
//        delegate?.buutonTapped(index: 1)
//    }
//}
//
//// 我们创建一个记录所有事件的结构体 它会被复制 还会失去 结构体信息
//
//struct TapLogger: AlertViewDelegate {
//    var taps: [Int] = []
//    mutating func buutonTapped(index: Int) {
//        taps.append(index)
//    }
//}
//
//let av = AlertView()
//var logger = TapLogger()
//av.delegate = logger
//logger.taps

// 两者都有缺陷  那么 就 使用函数而非代理

class AlertView {
    var buttons: [String]
    var callback: ((Int) -> Void)?
    init(buttons: [String] = ["ok", "cancel"]) {
        self.buttons = buttons
    }

    func fire() {
        callback?(1)
    }
}

struct TapLogger {
    var taps: [Int] = []
    mutating func buutonTapped(index: Int) {
        taps.append(index)
    }
}

let av = AlertView()

var logger = TapLogger()

av.callback = { logger.buutonTapped(index: $0) }
// 额外好处是命名解耦  甚至我们可以用匿名函数

class Test {
    func buttonTapped(index: Int) {
        print(index)
    }
}


let test = Test()
av.callback = test.buttonTapped

/// 这样av 就强引用了一个 test
av.callback = { [weak test] index in
    test?.buttonTapped(index: index)
}

inout 参数和可变方法

�* 不要被C和C++ 的& 误导 swift 中 inout 不是执行引用传递 而是值传递。它把值传入修改最后替换原来的值。

*为了了解什么样表达式可以当做inout 先要了解 lvalue:描述一个内存地址(array[0]); rvalue :描述一个值(4)

*对于inout函数 传入的是lvalue, 我们不可能对一个 rvlaue修改

func increment(value: inout Int) {
    value += 1
}

var i = 0 // 此处不能使用let
increment(value: &i)

var array = [1, 2, 3]

increment(value: &array[0]) // 所有的下标都适用 所有 有 get 和 set 都可以

struct Point {
    var x: Int
    var y: Int
}

var po = Point.init(x: 1, y: 2)
increment(value: &po.x)


extension Point {
    var square: Int {
        return x*x + y*y
    }
}

/// 嵌套函数inout

func incrementTenTimes(value: inout Int) {
    func inc() {
        value += 1
    }
    for _ in 0..<10 {
        inc()
    }
}

var x = 0
increment(value: &x) //不能让这个inout参数逃逸
// inout的值 会在函数返回之前复制回去 那么函数返回之后怎么办 值改变后在复制吗?资源不存在怎么办

//func escapeIncrement(value: inout Int) -> () -> () {
//    func inc () {
//        value += 1
//    }
//    return inc // 不行
//}

计算属性和下标

  • 有两种方法和其他方法是不同的 那就是 计算属性和下标
  • 计算属性看起来像属性 但是它不使用内存, 而下标则是遵守特殊规定 定义和调用的方法
import CoreLocation
//struct GPSTrak {
//    var record: [(CLLocation, Date)] = []
//}

struct GPSTrack {
    private(set) var record: [(CLLocation, Date)] = [] // 外部只读 内部可读写
}

extension GPSTrack {
    var date: [Date] { // 计算属性
        return record.map{ $0.1 }
    }
}

/// 延时储存属性 自动声明为mutating 因此必须声明为var

class GPSTrackViewController: UIViewController {
    var track = GPSTrack() // track发生变化 preview 不会自动更新.

    lazy var preview: UIImage = { // 可以把它 推迟到首次调用
        for po in self.track.record {
            // 昂贵计算
        }
        return UIImage()
    }()
}

自动闭包

  • 例如&&这种操作符叫短路求值 只有左边true时右边才会求值
  • 几乎所有的语言 短路求值都是内建在语言的值中无法自己定义
  • swift支持闭包 我们可以通过闭包模拟短路求值
let events = [2, 4, 6]

if !events.isEmpty && events[0] > 10 {
    // 执行
}

func and(_ l: Bool, _ r: @autoclosure () -> Bool ) -> Bool { // && 类似的函数
    guard l else {
        return false
    }
    return r()
}

if and(events.isEmpty, events[0] > 10) {
    // 执行
}

/// 自动闭包实 条件true 现日志输出

func log(condition: Bool,
         message: @autoclosure () -> String,
         file: String = #file,
         function: String = #function,
         line: Int = #line) {
    if condition {
        return
    }
    print("myAssert failed:\(message()), \(file):\(function):(line:\(line))")
}

log(condition: true, message: "this is test")
log(condition: false, message: "this is test")

/// 在处理闭包试 我们要格外小心内存 为了避免循环引用我们使用 [weak xxx]
/// 一个保存在某个地方等待调用的闭包称为 逃逸闭包(比如函数返回以后), 而像map这种闭包被直接使用不会改变闭包中值捕获的引用计数
/// 是否要声明 @escaping 编译器会验证

你可能感兴趣的:(swift-函数)