22、Swift 闭包的使用与优势

Swift 闭包的使用与优势

1. 单语句闭包的简化

在 Swift 中,如果闭包体仅由单个语句组成,我们可以省略 return 关键字,该语句的结果将自动返回。例如:

let clos7 = {
    (first: Int, second: Int) -> Int in
    first + second
}

在这个例子里,闭包接受两个 Int 类型的参数,并返回一个 Int 类型的值。闭包体中的唯一语句是将第一个参数与第二个参数相加。这里没有在加法语句前使用 return 关键字,Swift 会识别这是一个单语句闭包,并自动返回结果,就好像我们添加了 return 关键字一样。需要注意的是,语句的结果类型必须与闭包的返回类型相匹配。

2. 结合数组算法使用闭包

我们以 map 算法为例,先定义一个数组:

let guests = ["Jon", "Kim", "Kailey", "Kara"]

以下是几种使用闭包与 map 算法的示例:
- 打印问候语

guests.map({
    (name: String) -> Void in
    print("Hello \(name)")
})

使用简写语法可简化为:

guests.map({print("Hello \($0)")})
  • 返回包含问候语的新数组
var messages = guests.map({
    (name:String) -> String in
    return "Welcome \(name)"
})

当我们需要多次使用闭包或者在不同数组上使用相同闭包时,可以将闭包赋值给常量或变量,例如:

let greetGuest = {
  (name:String) -> Void in
    print("Hello guest named \(name)")
}
let sayGoodbye = {
  (name:String) -> Void in
    print("Goodbye \(name)")
}
guests.map(greetGuest)
guests.map(sayGoodbye)

我们还可以在闭包中对数组进行过滤操作,例如:

let greetGuest2 = {
  (name:String) -> Void in
    if (name.hasPrefix("K")) {
      print("\(name) is on the guest list")
    } else {
    print("\(name) was not invited")
    }
}

闭包能够捕获和存储其定义上下文内的任何变量或常量的引用。例如下面的温度计算示例:

func temperatures(calculate:(Int)->Void) {
    var tempArray = [72,74,76,68,70,72,66]
    tempArray.map(calculate)
}
func testFunction() {
    var total = 0
    var count = 0
    let addTemps = {
      (num: Int) -> Void in
      total += num
      count++
    }
    temperatures(addTemps)
    print("Total: \(total)")
    print("Count: \(count)")
    print("Average: \(total/count)")
}

testFunction 中, addTemps 闭包能够更新和使用在其定义上下文中的 total count 变量,即使该闭包在 temperatures 函数中被调用。

3. 独立闭包与良好的代码风格

闭包使我们能够将代码的数据部分与用户界面和业务逻辑部分真正分离,有助于创建专注于数据检索的可重用类。以下是一个示例:

class Guests {
    var guestNames = ["Jon","Kim","Kailey","Kara","Buddy","Lily","Dash"]
    typealias UseArrayClosure = [String] -> Void   
    func getGuest(handler:UseArrayClosure) {
        handler(guestNames)
    }
}

在这个 Guests 类中,我们定义了一个 guestNames 数组,并创建了一个类型别名 UseArrayClosure getGuest 方法接受一个符合 UseArrayClosure 类型的闭包作为参数,并执行该闭包。

假设我们要在 UITableView 中显示这些名字,在视图控制器中可以这样做:

@IBOutlet var tableView:UITableView?
var tableData: [String]?
func getData() {
  let dataClosure: Guests.UseArrayClosure = {
    self.tableData = $0
    if let tView = self.tableView {
      tView.reloadData()
    }
  }
  let guests = Guests()
  guests.getGuest(dataClosure)
}

通过使用闭包,我们可以异步调用网络服务,当数据返回时,闭包会自动执行,避免了 UI 冻结的问题。

4. 改变功能

闭包还能让我们动态改变类的功能。例如,定义一个 TestClass

class TestClass {
  typealias getNumClosure = ((Int, Int) -> Int)
  var numOne = 5
  var numTwo = 8
  var results = 0
  func getNum(handler: getNumClosure) -> Int {
    results = handler(numOne,numTwo)
    return results
  }
}

然后定义几个符合 getNumClosure 类型的闭包:

var max: TestClass.getNumClosure = {
  if $0 > $1 {
    return $0
  } else {
    return $1
  }
}
var min: TestClass.getNumClosure = {
  if $0 < $1 {
    return $0
  } else {
    return $1
  }
}
var multiply:  TestClass.getNumClosure = {
  return $0 * $1
}
var second: TestClass.getNumClosure = {
  return $1
}
var answer: TestClass.getNumClosure = {
  var tmp = $0 + $1
  return 42
}

我们可以将这些闭包传递给 TestClass getNum 方法,以改变函数的功能:

var myClass = TestClass()
myClass.getNum(max)
myClass.getNum(min)
myClass.getNum(multiply)
myClass.getNum(second)
myClass.getNum(answer)

不同闭包的执行结果如下表所示:
| 闭包名称 | 结果 |
| ---- | ---- |
| max | 8 |
| min | 5 |
| multiply | 40 |
| second | 8 |
| answer | 42 |

5. 根据结果选择闭包

在某些情况下,我们会根据一定的逻辑选择执行不同的闭包。例如,定义一个 TestClass

class TestClass {
  typealias ResultsClosure = ((String) -> Void)
  func isGreater(numOne: Int, numTwo:Int, successHandler: 
    ResultsClosure, failureHandler: ResultsClosure) {
    if numOne > numTwo {
      successHandler("\(numOne) is greater than \(numTwo)")
    }
    else {
      failureHandler("\(numOne) is not greater than \(numTwo)")
    }
  }
}

再定义两个闭包:

var success: TestClass. ResultsClosure = {
    print("Success: \($0)")
}
var failure: TestClass. ResultsClosure = {
    print("Failure: \($0)")
}

isGreater 方法中,根据 numOne numTwo 的大小关系,选择执行 successHandler failureHandler 闭包。整个流程可以用以下 mermaid 流程图表示:

graph TD;
    A[开始] --> B{numOne > numTwo?};
    B -- 是 --> C[执行 successHandler];
    B -- 否 --> D[执行 failureHandler];
    C --> E[结束];
    D --> E;

综上所述,闭包在 Swift 中具有强大的功能和广泛的应用场景,能够帮助我们编写更加灵活、可维护的代码。无论是结合数组算法、独立使用,还是用于改变类的功能和根据结果选择执行,闭包都展现出了其独特的优势。

Swift 闭包的使用与优势

6. 闭包在不同场景下的总结与对比

为了更清晰地了解闭包在不同场景下的应用,我们可以通过以下表格进行总结对比:
| 应用场景 | 代码示例 | 特点与优势 |
| ---- | ---- | ---- |
| 单语句闭包简化 | let clos7 = { (first: Int, second: Int) -> Int in first + second } | 省略 return 关键字,代码简洁,提高开发效率 |
| 结合数组算法 | guests.map({print("Hello \($0)")}) | 可对数组元素进行批量处理,支持过滤、转换等操作 |
| 独立闭包 | 见 Guests 类和 getData 函数示例 | 分离数据与业务逻辑,便于创建可重用类,支持异步操作 |
| 改变功能 | 见 TestClass 和多个闭包示例 | 动态改变类的功能,增强代码灵活性 |
| 根据结果选择闭包 | 见 TestClass isGreater 方法示例 | 根据不同条件执行不同闭包,实现复杂逻辑控制 |

7. 闭包使用的注意事项

在使用闭包时,我们还需要注意以下几点:
- 类型匹配 :闭包的参数类型和返回类型必须与使用场景相匹配,否则会导致编译错误。例如,在 TestClass getNum 方法中,传递的闭包必须符合 getNumClosure 类型。
- 捕获变量 :闭包会捕获其定义上下文内的变量和常量,可能会导致内存泄漏。如果闭包持有对某个对象的强引用,而该对象又持有闭包的强引用,就会形成循环引用。可以使用弱引用( weak )或无主引用( unowned )来避免这种情况。
- 代码可读性 :虽然闭包可以使代码更简洁,但过度使用简写语法可能会降低代码的可读性。在编写闭包时,要根据实际情况选择合适的语法,确保代码易于理解和维护。

8. 闭包的进阶应用思路

闭包的应用不仅仅局限于上述场景,我们还可以进一步拓展其应用范围:
- 链式调用 :可以将多个闭包组合起来进行链式调用,实现更复杂的操作。例如,对数组元素先进行过滤,再进行转换,最后进行排序。

let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let result = numbers.filter({ $0 % 2 == 0 }).map({ $0 * 2 }).sorted()
print(result)
  • 自定义算法 :基于闭包,我们可以自定义一些复杂的算法。例如,实现一个自定义的排序算法,根据不同的比较规则对数组进行排序。
func customSort<T>(_ array: [T], by compare: (T, T) -> Bool) -> [T] {
    var sortedArray = array
    for i in 0..<sortedArray.count {
        for j in i + 1..<sortedArray.count {
            if compare(sortedArray[j], sortedArray[i]) {
                sortedArray.swapAt(i, j)
            }
        }
    }
    return sortedArray
}

let unsortedNumbers = [5, 3, 8, 1, 2]
let sortedNumbers = customSort(unsortedNumbers) { $0 < $1 }
print(sortedNumbers)
9. 闭包在实际项目中的应用流程

在实际项目中,使用闭包的一般流程如下:
1. 需求分析 :确定项目中哪些部分可以使用闭包来实现,例如异步数据加载、动态功能切换等。
2. 闭包设计 :根据需求设计闭包的参数类型和返回类型,确保其与使用场景相匹配。
3. 代码实现 :编写闭包代码,并将其应用到相应的功能模块中。
4. 测试与优化 :对使用闭包的功能进行测试,检查是否存在内存泄漏、类型不匹配等问题,并进行优化。

以下是一个 mermaid 流程图,展示了闭包在实际项目中的应用流程:

graph LR;
    A[需求分析] --> B[闭包设计];
    B --> C[代码实现];
    C --> D[测试与优化];
    D --> E{是否满足需求?};
    E -- 是 --> F[项目上线];
    E -- 否 --> B;

通过以上对闭包的详细介绍和分析,我们可以看到闭包在 Swift 开发中具有重要的地位和广泛的应用前景。合理使用闭包可以使我们的代码更加灵活、高效、可维护,帮助我们更好地应对各种复杂的开发需求。

先展示下效果 https://pan.quark.cn/s/a4b39357ea24 遗传算法 - 简书 遗传算法的理论是根据达尔文进化论而设计出来的算法: 人类是朝着好的方向(最优解)进化,进化过程中,会自动选择优良基因,淘汰劣等基因。 遗传算法(英语:genetic algorithm (GA) )是计算数学中用于解决最佳化的搜索算法,是进化算法的一种。 进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择、杂交等。 搜索算法的共同特征为: 首先组成一组候选解 依据某些适应性条件测算这些候选解的适应度 根据适应度保留某些候选解,放弃其他候选解 对保留的候选解进行某些操作,生成新的候选解 遗传算法流程 遗传算法的一般步骤 my_fitness函数 评估每条染色体所对应个体的适应度 升序排列适应度评估值,选出 前 parent_number 个 个体作为 待选 parent 种群(适应度函数的值越小越好) 从 待选 parent 种群 中随机选择 2 个个体作为父方和母方。 抽取父母双方的染色体,进行交叉,产生 2 个子代。 (交叉概率) 对子代(parent + 生成的 child)的染色体进行变异。 (变异概率) 重复3,4,5步骤,直到新种群(parentnumber + childnumber)的产生。 循环以上步骤直至找到满意的解。 名词解释 交叉概率:两个个体进行交配的概率。 例如,交配概率为0.8,则80%的“夫妻”会生育后代。 变异概率:所有的基因中发生变异的占总体的比例。 GA函数 适应度函数 适应度函数由解决的问题决定。 举一个平方和的例子。 简的平方和问题 求函数的最小值,其中每个变量的取值区间都是 [-1, ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值