要了解swift的函数和闭包,首先要了解三件事
- 函数可以像String 和 Int 那样可以赋值给变量,也可以作为另一个函数的参数和返回值。(函数是头等对象,这一点最重要)
- 函数能捕获存在于其作用域之外的变量。
- 有两种方式可以创建函数。一种是使用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 编译器会验证