Swift5.x入门20--编程范式

函数式编程

  • 函数式编程:是一种编程范式,也就是如何编写程序的方法论;
  • 其主要思想为:把计算过程尽量分解成一系列可复用的函数调用;
  • 其主要特征为:函数与其他数据类型一样的地位,可以赋值给其他变量,也可以作函数的参数,函数的返回值;
import Foundation

let num = 1

//第一种实现方法
func add(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }
func sub(_ v1: Int,_ v2: Int) -> Int { v1 - v2}
func multipe(_ v1: Int,_ v2: Int) -> Int { v1 * v2 }
func divide(_ v1: Int,_ v2: Int) -> Int { v1 / v2 }
func mod(_ v1: Int,_ v2: Int) -> Int { v1 % v2 }

let reslut = divide(mod(sub(multipe(add(num, 3), 5), 1), 10), 2)
print(reslut)//4

//第二种实现方法
//返回值类型 (Int) -> Int v1+(Int参数)
//add1函数的参数v1,作为内部闭包函数的实现的一部分;
//$0是闭包函数的参数 也就是当前函数的 (返回值函数) 的参数;
func add1(_ v1: Int) -> (Int) -> Int { {$0 + v1} }
func sub1(_ v1: Int) -> (Int) -> Int { {$0 - v1} }
func multipe1(_ v1: Int) -> (Int) -> Int { { $0 * v1 } }
func divide1(_ v1: Int) -> (Int) -> Int { { $0 / v1 } }
func mod1(_ v1: Int) -> (Int) -> Int { { $0 % v1 } }

let fn1 = add1(3)
let fn2 = multipe1(5)
let fn3 = sub1(1)
let fn4 = mod1(10)
let fn5 = divide1(2)
let result1 = fn5(fn4(fn3(fn2(fn1(num)))))
print("result1 = ",result1)//4

//第三种实现方法:
//函数合成
//f1与f2不是在composite内部调用的 是在闭包中调用的 属于逃逸闭包,则需加@escaping
func composite(_ f1: @escaping (Int) -> Int,_ f2: @escaping (Int) -> Int) -> (Int) -> Int {
    {
        f2(f1($0))
    }
}

//(Int) -> Int
let fn6 = composite(add1(3), multipe1(5))(num)
print("fn6 = ",fn6) //20

//自定义运算符
infix operator >>> : AdditionPrecedence
func >>>(_ f1: @escaping (Int) -> Int,_ f2: @escaping (Int) -> Int) -> (Int) -> Int {
    {
        f2(f1($0))
    }
}  
let fn7 = add1(3) >>> multipe1(5) >>> sub1(1) >>> mod1(10) >>> divide1(2)
print("result2 = ",fn7(num))//4
import Foundation

let num = 1

///add函数的返回值 是一个 (参数为Int,返回值为Int) 的函数
///其中$0 是add函数的 [返回值函数] 的入参 也就是下面的入参num
func add(_ v1: Int) -> (Int) -> Int { { $0 + v1 } }

let functionAdd = add(3)
let addNum = functionAdd(num)

print(addNum)
高阶函数
  • 高阶函数是至少满足下列一个条件的函数:
    • 接收一个或者多个函数,作为输入参数;
    • 返回一个函数;
  • 在函数式编程中,到处都是高阶函数;
柯里化
  • 将一个接收多个参数的函数变换成一系列只接收单个参数的函数;
func add(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }

//将函数add柯里化
func add1(_ v1: Int) -> (Int) -> Int { {$0 + v1} }
  • add柯里化后,变成只有一个入参,其返回值也是一个函数;
//实现两个数相加
func add1(_ v1: Int,_ v2: Int) -> Int { v1 + v2 }

//add1柯里化
//最完整的写法
func add_y(_ v1: Int) -> (Int) -> Int {
    return {
        a in
        return v1 + a
    }
}

func add2(_ v1: Int) -> (Int) -> Int {
    {
        a in
        return v1 + a
    }
}


func add_x(_ v1: Int) -> (Int) -> Int {
    return {
        a in
        v1 + a
    }
}


func add3(_ v1: Int) -> (Int) -> Int {
    {
        a in a + v1
    }
}

func add4(_ v1: Int) -> (Int) -> Int {
    {
        $0 + v1
    }
}

let r = add3(10)(20)

//实现3个数相加
func add(_ v1: Int,_ v2: Int,_ v3: Int) -> Int { v1 + v2 + v3 }
let r1 = add(10, 20, 30)
print(r1)

//add柯里化
func add5(_ v3: Int) -> (Int) -> (Int) -> Int{
    return {
        v2 in
        return {
            v1 in
            return v1 + v2 + v3
        }
    }
}

let k = add5(10)(20)(30)
print(k)
  • 实现传一个这种(A,B) -> (C)泛型函数,返回对应的柯里化函数,代码如下所示:
func add1(_ v1: Int,_ v2: Int) -> Int { v1 + v2 } //10+20

////将传入的函数 直接柯里化
func currying(_ fn: @escaping (A,B) -> C) -> (A) -> (B) -> (C) {
    return {
        a in
        return {
            b in
            return fn(a,b)
        }
    }
}

let res = currying(add1)(10)(20)
print(res) //30
  • 参数为 (A,B) -> C 是一个函数类型,最终返回的是柯里化函数,即只有一个参数,返回值是一个函数的 函数类型 (A) -> (B) -> C;
func add1(_ v1: Int,_ v2: Int) -> Int { v1 + v2 } //10+20

prefix func ~(_ fn: @escaping (A,B) -> C) -> (A) -> (B) -> C 
{
    return {
        a in
        return {
            b in
            return fn(a,b)
        }
    }
}

let res1 = (~add1)(10)(20)
print(res1)
  • 自定义一个运算符~,用来获取函数的柯里化函数的;

  • 实现传一个这种(A,B,C) ->D 的泛型函数,返回对应的柯里化函数,代码如下所示:

func add(_ v1: Int,_ v2: Int,_ v3: Int) -> Int { v1 + v2 - v3 }

prefix func ~(_ fn: @escaping(A,B,C) -> D) -> (A) -> (B) -> (C) -> D {
    return {
        a in
        return {
            b in
            return {
                c in
                return fn(a,b,c)
            }
        }
    }
}

print((~add)(10)(20)(50)) //-20
函子
  • 像Array,optional这样支持map运算的类型,称为函子;
public func map(_ transform: (Element) -> T) -> Array
  • 取出数组中的元素Element,经过transform函数的逻辑运算,最后再存入数组中;
适用函子
单子

面向协议编程

  • 面向协议编程简称POP,是Swift的一种编程范式,Apple与2015年WWDC提出;
  • 在Swift标准库中,能见到大量POP的影子;
  • Swift也是面向对象的编程语言,简称OOP;
  • POP能弥补OOP一些上次的不足;
利用协议实现前缀功能
  • 第一个阶段:给定一个字符串,计算出字符为数字的个数,定义一个全局方法numberCount
var str = "1234test1234"

func numberCount(_ str: String) -> Int {
    var count = 0
    for c in str {
        if (("0"..."9").contains(c)) {
            count += 1
        }
    }
    return count
}

print(numberCount(str))
  • 第二个阶段:给Swift字符串类String,扩展一个方法;
var str = "1234test1234"

extension String {
    var numberCount: Int {
        var count = 0
        for c in self where ("0"..."9").contains(c){
                count += 1
        }
        return count
    }
}

print(str.numberCount)
  • 第三个阶段:给字符串的扩展方法numberCount,添加指定前缀yy,表明此拓展方法是自定义的,与系统的区分开来;
struct YY {
    var string: String
    init(_ string: String) {
        self.string = string
    }
    var numberCount: Int {
        var count = 0
        for c in string where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
}

extension String {
    var yy: YY {
        return YY(self)
    }
}

print(str.yy.numberCount)
  • 第四个阶段:给所有类型添加指定前缀yy,使用泛型技术来实现;
struct YY {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

class Person {}
extension Person {
    //计算实例属性
    var yy: YY { YY(self) }
    //计算类型属性
    static var yy: YY.Type { YY.self }
}

extension String {
    //计算实例属性
    var yy: YY { YY(self) }
    //计算类型属性
    static var yy: YY.Type { YY.self }
}

struct Dog {}
extension Dog {
    //计算实例属性
    var yy: YY { YY(self) }
    //计算类型属性
    static var yy: YY.Type { YY.self }
}

//给具体的类进行扩展
extension YY where Base == String {
    //计算属性
    var numberCount: Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    //类型方法
    static func test() {
        print("test")
    }
}

extension YY where Base: Person {
    //实例方法
    func run() {
        print("run")
    }
    //类型方法
    static func walk () {
        print("walk")
    }
}

extension YY where Base == Dog {
    func spark() {
        print("spark")
    }

    static func gun() {
        print("gun")
    }
}

var str: String = "123456ty"
var person: Person = Person()
var dog: Dog = Dog()

print(str.yy.numberCount)
String.yy.test()
person.yy.run()
Person.yy.walk()
dog.yy.spark()
Dog.yy.gun()
  • 第五个阶段:添加指定前缀,扩展所有类型,利用协议YYCompatible抽取添加前缀属性的方法为公共方法到协议扩展中extension YYCompatible
struct YY {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

protocol YYCompatible {}
extension YYCompatible {
    //计算实例属性
    var yy: YY {
        set {}
        get { YY(self) }
    }
    //计算类型属性
    static var yy: YY.Type {
        set {}
        get { YY.self }
    }
}

class Person {}
extension Person: YYCompatible{}

extension String: YYCompatible {}

struct Dog {}
extension Dog: YYCompatible {}

//给具体的类进行扩展
extension YY where Base == String {
    //计算属性
    var numberCount: Int {
        var count = 0
        for c in base where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    //类型方法
    static func test() {
        print("test")
    }
}

extension YY where Base: Person {
    //实例方法
    func run() {
        print("run")
    }
    //类型方法
    static func walk () {
        print("walk")
    }
}

extension YY where Base == Dog {
    //mutating表示可以修改YY的内存数据,即YY可修改
    mutating func spark() {
        print("spark")
    }

    static func gun() {
        print("gun")
    }
}

var str = "123456789ss"
var person = Person()
var dog = Dog()

print(str.yy.numberCount)
String.yy.test()

person.yy.run()
Person.yy.walk()

dog.yy.spark()
Dog.yy.gun()
  • 第六个阶段:添加指定前缀,扩展所有类型,利用协议YYCompatible抽取添加前缀属性的方法为公共方法到协议扩展中extension YYCompatible
  • NSString,NSMutableString同样支持numberCount功能;
struct YY {
    var base: Base
    init(_ base: Base) {
        self.base = base
    }
}

protocol YYCompatible {}
extension YYCompatible {
    //计算实例属性
    var yy: YY {
        set {}
        get { YY(self) }
    }
    //计算类型属性
    static var yy: YY.Type {
        set {}
        get { YY.self }
    }
}

class Person {}
extension Person: YYCompatible{}

struct Dog {}
extension Dog: YYCompatible {}

extension String: YYCompatible {}
extension NSString: YYCompatible {}

//给具体的类进行扩展
extension YY where Base: ExpressibleByStringLiteral {
    //计算属性
    var numberCount: Int {
        var count = 0
        for c in (base as! String) where ("0"..."9").contains(c) {
            count += 1
        }
        return count
    }
    //类型方法
    static func test() {
        print("test")
    }
}

//extension YY where Base: NSString {
//    //计算属性
//    var numberCount: Int {
//        var count = 0
//        for c in base as String where ("0"..."9").contains(c) {
//            count += 1
//        }
//        return count
//    }
//}

extension YY where Base: Person {
    //实例方法
    func run() {
        print("run")
    }
    //类型方法
    static func walk () {
        print("walk")
    }
}

extension YY where Base == Dog {
    //mutating表示可以修改YY的内存数据,即YY可修改
    mutating func spark() {
        print("spark")
    }
    
    static func gun() {
        print("gun")
    }
}

var str: String = "123456789ss"
var str1: NSString = "34233222er"
var str2: NSMutableString = "23re21e34"

var person = Person()
var dog = Dog()

print(str.yy.numberCount)
String.yy.test()
print(str1.yy.numberCount)
print(str2.yy.numberCount)


person.yy.run()
Person.yy.walk()

dog.yy.spark()
Dog.yy.gun()
  • 因为String,NSString,NSMutableString都遵循ExpressibleByStringLiteral协议,所以利用它来定义所有字符串类型,包括OC字符串与Swift字符串;
利用协议进行类型判断
  • 判断value是否是数组类型
func isArray(_ value: Any) -> Bool {
//    return value is [Any]
    return value is Array
}

print(isArray([1,2]))
print(isArray(["1",2]))
print(isArray(NSArray()))
print(isArray(NSMutableArray()))
print(isArray("123456"))
  • 判断类型是否是数组类型;
func isArrayType(_ type: Any.Type) -> Bool {
    return type is [Any].Type
}

print(isArrayType([Int].self)) //false
print(isArrayType([Any].self)) //true
print(isArrayType(NSArray.self)) //false
print(isArrayType(NSMutableArray.self)) //false
print(isArrayType(String.self)) //false
  • 发现这种做法行不通,可使用协议来解决:
protocol ArrayType {}

extension Array: ArrayType {}
extension NSArray: ArrayType {}

////是否是数组类型
func isArrayType(_ type: Any.Type) -> Bool {
    return type is ArrayType.Type
}

print(isArrayType([Int].self)) //true
print(isArrayType([Any].self)) //true
print(isArrayType(NSArray.self)) //true
print(isArrayType(NSMutableArray.self)) //true
print(isArrayType(String.self)) //false

响应式编程

  • 响应式编程,简称RP;
  • 是一种编程范式,于1997年提出,可以简化异步编程,提供更优雅的数据绑定;
  • 一般与函数式融合在一起,所以也叫做函数式响应编程,简称FRP;
  • 比较成熟的响应式框架有:
    • ReactiveCocoa:有OC,Swift版本;
    • ReactiveX :有RxJava,RxKotlin,RxSwift,RxPhp,RxGo等等;
RxSwift
  • 源码:https://github.com/ReactiveX/RxSwift
  • 中文文档:https://beethOven.github.io/RxSwift-Chinese-Documentation/
  • Cocoapods安装RxSwift,Podfile文件内容如下:
use_frameworks!

target 'Swift20_RxSwift' do

  pod 'RxSwift', '6.2.0'
  pod 'RxCocoa', '6.2.0'
  
end
RxSwift的核心角色
  • Observable:负责发送事件(Event);
  • Observer:负责订阅Observable,监听Observable发送的事件(Event);
  • Event有三种:
@frozen public enum Event {
    /// Next element is produced.
    case next(Element)

    /// Sequence terminated with an error.
    case error(Swift.Error)

    /// Sequence completed successfully.
    case completed
}
import UIKit
import RxCocoa
import RxSwift

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
     
        //定义一个发送事件器 Observable
        let observable = Observable.create { (observer) -> Disposable in
            observer.onNext(11)
            return Disposables.create()
        }
    
        //可以看成是闭包在 订阅监听事件
        //Disposable
        let disposable = observable.subscribe { (event) in
            switch event{
            case .next(let element):
                print("next",element)
            case .error(let error):
                print("error",error)
            case .completed:
                print("completed")
            }
        }
        //取消订阅
        disposable.dispose()
        
        observable.subscribe { (element) in
            print("next",element)
        } onError: { (error) in
            print("error",error)
        } onCompleted: {
            print("completed")
        } onDisposed: {
            print("disposed")
        }.disposed(by: bag) //当bag销毁时,取消订阅
    }
}
Disposable
  • 每当observable被订阅的时候,都会返回一个Disposable实例对象,当Disposable实例对象调用dispose,就相当于取消订阅;

  • 在不需要接收事件订阅时,建议取消订阅,释放资源;

  • 实现按钮每隔1秒重复显示/消失;

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    
    let bag = DisposeBag()
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
     
        //实现每隔1秒钟button显示/消失
        //创建发送事件器
        let observable = Observable.timer(.seconds(2), period: .seconds(1), scheduler: MainScheduler.instance)
        
        //事件订阅监听者
        let binder = Binder(button) { (button, value) in
            button.isHidden = value
        }
        
        //事件订阅监听者 订阅监听事件
        observable.map { $0 % 2 == 0 }.bind(to: binder).disposed(by: bag)
        
    }
}
RxSwift状态监听
  • 按钮点击
import UIKit
import RxCocoa
import RxSwift


class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
     
        button.rx.controlEvent(.touchUpInside).subscribe(onNext: {
            print("按钮点击")
        }).disposed(by: bag)
        
    }
}
  • Rx实现KVO监听实例对象的存储属性变化;
import UIKit
import RxCocoa
import RxSwift

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
     
        var dog: Dog = Dog()
        dog.rx.observe(String.self, "name").subscribe(onNext: { name in
            print("name is",name ?? nil)
        }).disposed(by: bag)
        
        dog.name = "Tom"
        dog.name = "Jack"
    }
}
  • Rx实现通知监听;
import UIKit
import RxCocoa
import RxSwift

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    
    let bag = DisposeBag()
    
    override func viewDidLoad() {
        super.viewDidLoad()
     
        NotificationCenter.default.rx.notification(UIApplication.didEnterBackgroundNotification).subscribe(onNext: { notification in
            print("App进入后台",notification)
        }).disposed(by: bag)
    }
}

Swift中常见的高阶函数

Map函数
  • map函数作用于Collection中的每一个元素,然后返回一个新的Collection;
  • map函数可以对数组中的每一个元素做一次处理,如同遍历的作用;
  • map函数接受一个闭包作为参数, 然后它会遍历整个数组,并对数组中每一个元素执行闭包中定义的操作,相当于对数组中的所有元素做了一个映射;
  • map函数可以对一个集合类型的所有元素做一个映射操作;
  • map函数不会修改实例值, 而是新建一个拷贝;
需求一:对数组中的所有元素 大写转小写;
import Foundation

///数组中元素 大写->小写
let strings_00 = ["YAN","ZI"]
var strings_01: [String] = []

///普通常规做法
for str in strings_00 {
    strings_01.append(str.lowercased())
}
print(strings_01)

///使用Swift高阶函数 -- Map
///map函数中 闭包最完整的写法
var strings_02 = strings_00.map { (value: String) -> String in
    return value.lowercased()
}
print(strings_02)

var strings_03 = strings_00.map { (value) -> String in
    return value.lowercased()
}
print(strings_03)

var strings_04 = strings_00.map { (value) in value.lowercased() }
print(strings_04)

///map函数中 闭包最简洁的写法
var strings_05 = strings_00.map { $0.lowercased() }
print(strings_05)
flatMap函数
  • flatMap函数依然会遍历数组的元素,并对这些元素执行闭包中定义的操作,与map函数不同的是,它对最终的结果进行了所谓的“压平”操作,多维数组flatMap之后,变成一维数组
  • flatMap函数返回后,数组的元素会过滤nil值,同时它会把Optional解包
import Foundation

///二维数组的 降维处理
let nums = [[1,2,3],[4,5,6]]

var nums_00 = nums.map{$0}
var nums_01 = nums.flatMap{$0}

print("map: \(nums_00)") //map: [[1, 2, 3], [4, 5, 6]]
print("flatMap: \(nums_01)") //flatMap: [1, 2, 3, 4, 5, 6]

///过滤nil值
let strings = ["yan","zi","fang",nil]
var strings_00 = strings.map({$0})
var strings_01 = strings.compactMap{$0}

print("map: \(strings_00)") //map: [Optional("yan"), Optional("zi"), Optional("fang"), nil]
print("flatMap: \(strings_01)") //flatMap: ["yan", "zi", "fang"]

//可选项
let str_00: String? = String(100)
///默认没有解包操作
let result_00 = str_00.map( {Int($0)} )
///默认有解包操作
let result_01 = str_00.flatMap( {Int($0)} )

print(result_00) //Optional(Optional(100))
print(result_01) //Optional(100)
  • flatMap函数对数据元素默认有解包操作,而map函数没有
filter函数
  • filter函数:创建一个新数组,将满足闭包表达式的元素 添加到新数组中,最终返回新数组;
import Foundation

let nums = [1,2,3,4,5,6]
///过滤出奇数 返回的是奇数数组
let result = nums.filter({ $0 % 2 != 0 })
print(result)

///过滤出偶数 返回的是偶数数组
let result1 = nums.filter( {$0 % 2 == 0} )
print(result1)
forEach函数
  • forEach函数:遍历;
import Foundation

let nums = [1,2,3,4,5,6]

///遍历元素
nums.forEach { print($0) }

///遍历元素 索引
nums.enumerated().forEach {
    print("index: \($0), element: \($1)")
}
reduce函数
  • 首先来查看一下reduce函数底层实现:
@inlinable
  public func reduce(
    _ initialResult: Result,
    _ nextPartialResult:
      (_ partialResult: Result, Element) throws -> Result
  ) rethrows -> Result {
    var accumulator = initialResult
    for element in self {
      accumulator = try nextPartialResult(accumulator, element)
    }
    return accumulator
  }
  • initialResult:初始值;
  • nextPartialResult:闭包表达式;
  • accumulator:累加器;
  • 执行工程:遍历集合,将累加器和元素传入闭包表达式进行指定操作,将结果重新赋值给累加器,最后将累加器返回;
import Foundation

let nums = [1,2,3,4,5]
//最完整的写法
let result = nums.reduce(10) { (pre, current) -> Int in
    return pre + current
}

let result01 = nums.reduce(10) { (pre, current) in
    pre + current
}

let result02 = nums.reduce(10, { $0 + $1 })
///尾随闭包写法
let result03 = nums.reduce(10) { $0 + $1 }
///简写
let result04 = nums.reduce(10, +)

print(result)
print(result01)
print(result02)
print(result03)
print(result04)
  • 在最完整的写法中,数值10是初始值,pre是每次迭代计算的部分结果current每次迭代集合中当前项的值,最终返回的结果是 数组中所有元素叠加与初始化值10的和
  • 其中 10 为累加器的初始值;
  • { $0 + $1 }为闭包表达式,其中 $0是累加器的结果值$1是数组中遍历元素
需求一:集合中的每个元素 * 2,返回一个新的集合
import Foundation

let nums = [1,2,3,4,5]

let result1 = nums.reduce(into: [Int]()) { (numbers, current) in
    numbers.append(current * 2)
}
print(result1)
//简写方式
let result2 = nums.reduce(into: [Int]()) {
    $0.append($1 * 2)
}
print(result2)
  • [Int]():表明reduce函数的返回值类型是一个整型数组,最终返回的是numbers数组;
需求二:找出数组中的最大值
import Foundation

let nums = [1,2,100,4,5]

let result1 = nums.reduce(0) { (pre, current) -> Int in
    print("\(pre) -- \(current)")
    return pre < current ? current : pre
}
print(result1) //100

///简写方式
let result2 = nums.reduce(0) { $0 < $1 ? $1 : $0 }
print(result2) //100
  • pre:遍历时 每次闭包表达式的返回值;
  • current:遍历时,数组中元素;
  • pre也是reduce函数 最终的返回值;
需求三:数组逆序
import Foundation

let nums = [1, 2, 3, 4, 5]

let result = nums.reduce([Int]()) { (pres, current) -> [Int] in
    return [current] + pres
}
print(result)

let result1 = nums.reduce([Int]()) {
    return [$1] + $0
}
print(result1)

/**
 accumulator = [Int]() 空数组
 数组合并
 [element] + []
      pres  pres
 [1] + [] = [1]
 [2] + [1] = [2,1]
 [3] + [2,1] = [3,2,1]
 [4] + [3,2,1] = [4,3,2,1]
 [5] + [4,3,2,1] = [5,4,3,2,1]
 */
  • 参数[Int]()是reduce函数的返回值类型,也是pres的数据类型;

参考文章

Swift底层进阶--018:高阶函数

你可能感兴趣的:(Swift5.x入门20--编程范式)