编辑代码

OMG, 闭包的参数标签可以省略,就连()也可以省略
方法名后直接跟的大括号

数组是二等类型吗?请看下面AI的回答
在Swift中,数组是一种二等类型(second-class type)。
这是因为Swift的数组类型在某些操作上受到了限制,无法直接进行一些高级操作,
例如在某些情况下无法作为字典的键或者以某些方式进行泛型编程。
总的来说,虽然Swift提供了许多高级特性,但是数组在某些情况下不能被当作完全的类型。
这也是为什么它被称为二等类型的原因。

呃,枚举是一等类型。。。
因为。。。
它们采用了很多在传统上只被类(class)所支持的特性,
例如计算属性,例如实例方法,例如构造函数,例如遵循协议。。。

枚举简单的说是一种数据类型,只不过是这种数据类型只包含自定义的特定数据,它是一组有共同特性的数据的集合。

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
//这是一个平凡无奇的字符串数组
let customerProvider = {customersInLine.remove(at: 0)}
//这是一个特殊写法,一个常量被赋值了一个函数,但是该函数虽有remove操作却不执行
print("String is \(customerProvider()), and count \(customersInLine.count)")
//这个时候,在打印的时候,把常量加上一个()就立即执行了,该remove的remove

func serve(customer customerProvider: () -> String){
    print("Remove the \(customerProvider()) now is \(customersInLine.count)")
}
//定义一个以函数作为参数的函数serve,它在打印中调用此参数函数
serve(customer: {customersInLine.remove(at: 0)})
//此时,调用了函数serve,同时一并实现了其函数参数的内容

func registerUser(username: String, completion: @escaping (Bool) -> Void) {
    // 封装验证用户名的操作
    let validateUsername = {
        // 这里可以是一些复杂的用户名验证逻辑
        return username.count > 3 && !username.isEmpty
    }

    // 封装发送验证邮件的操作
    let sendVerificationEmail = {
        // 模拟发送电子邮件
        print("Verification email sent to \(username)!")
        return true
    }

    // 封装存储用户信息的操作
    let saveUserInfo = {
        // 假设成功保存用户信息
        print("User \(username) info saved!")
        return true
    }

    // 执行每一个步骤
    if validateUsername() {
        if sendVerificationEmail() {
            if saveUserInfo() {
                completion(true)  // 注册成功
            } else {
                completion(false) // 存储失败
            }
        } else {
            completion(false) // 邮件发送失败
        }
    } else {
        completion(false) // 用户名无效
    }
}

// 使用闭包进行注册
registerUser(username: "testUser") { success in
    if success {
        print("Registration Successful!")
    } else {
        print("Registration Failed!")
    }
}
在这个例子中,通过将每个步骤封装为一个闭包,可以让代码的逻辑更加清晰,容易理解。
而且一旦需要修改某个步骤(例如更改验证逻辑或发送邮件的方式),你只需在相应闭包中进行更改,
而不需要重构整个函数。

func path(in rect: CGRect) -> Path {
        var path = Path()
        let radius = min(rect.width / 2, rect.height / 2)
        let center =  CGPoint(x: rect.width / 2, y: rect.height / 2)
        var nextPoint = CGPoint.zero
        
        let ithPoint: (Int) -> CGPoint = { i in
            let point = center + CGPoint(x: radius * sin(CGFloat(i) * CGFloat.pi * CGFloat(2) / CGFloat(pointsNum)),
                                         y: radius * cos(CGFloat(i) * CGFloat.pi * CGFloat(2) / CGFloat(pointsNum)))
            var direction = CGVector(point - center)
            direction = direction / direction.len()
            return point + direction * CGFloat(morphing[i >= pointsNum ? 0 : i])
        }

        for i in (0...pointsNum){
            nextPoint = ithPoint(i)
        }
        ……

         path.closeSubpath()
        return path
    }
在这段伪代码中,我问百度,在这里ithPoint为什么要使用闭包?百度答:
封装和复用:
将这段逻辑放入闭包中,允许您轻松重用该逻辑,只需简单地调用闭包。
例如,您可以在绘制形状时多次调用 ithPoint(i) 来得到不同的点。
简化代码:
通过将相关的计算逻辑聚合到一个闭包中,您可以保持代码的结构清晰,便于理解和维护。
上下文捕获:
在这个上下文中,闭包可以方便地访问外部变量如 center、radius、pointsNum 和 morphing,让代码更加简洁。
如果把这个逻辑拆分到多个函数中,就需要通过参数显式地传递这些值,反而可能导致更复杂的调用结构。
评:闭包是一种可以捕获和存储其上下文环境的函数;函数的作用是:把A转化为B;
以前从未真正理解过“上下文捕获”,这里懂了,哪怕你把它改成函数,是需要写很多参数的。
实践了一下,确实可以改写成函数,比如此例子为嵌套函数,但是有两个地方很烦,一个是函数申明时多个参数名,
另一个是函数调用时多个参数名。

发现计算属性和闭包在写法上很像:
var radius: CGFloat = outerSize / 2
当 outerSize 在对象生命周期中比较稳定,或你只需在初始化时计算一次,并且在之后的代码中 radius 不再更改时,
可以选择存储属性。
var radius: CGFloat { outerSize / 2 } 
当你希望 radius 始终反映 outerSize 的最新状态,且 outerSize 可能变化频繁时,
使用计算属性是一种更好的选择。
var radius = { outerSize / 2 }
可以控制何时执行计算,适用于更复杂的需求。