swift闭包底层本质

1、函数赋值给一个变量
func getFn(_ a: Int) -> Int {
  return a + 1
}

let funcVar = getFn
  • 当在把一个函数赋值给一个变量funcVar的时候,funcVar变量会存储两个东西
    • funcVar总共占用16个字节
    • 前八个字节存储着getFn的函数地址
    • 后八个字节存储着0
      • 这种情况下暂时是没用到的,因此是0这种空值
      • 当在闭包当中就有大大的用处
2、 闭包捕获一个变量时赋值给一个变量
func getFn() -> (Int) -> (Int) {
  var num = 0
  func plus(_ a: Int) -> Int {
    num += a
    return num
	}
  
  return testClosure
}

let plus = getFn()
plus(1)
plus(2)
  • 当把捕获一个变量的闭包赋值给一个变量plus的时候,plus变量也同样会存储两个东西
    • plus总共占用16个字节
    • 前八个字节存储着plus闭包的函数地址
    • 后八个字节存储捕获到的num的信息,并且此信息存储在堆空间
      • 此时的num已经被包装成一个对象了
      • 其拥有24个字节
      • 前八个字节存储着类似isa的东西
      • 中间八个字节存储着引用计数
      • 最后八个字节就是num
  • 当在调用plus(1)的时候,会找到其存储的前八个字节,也就是闭包的函数地址,进行函数调用
  • 调用的时候,会传入两个参数
    • 一个是1
    • 另一个就是捕获的堆空间的num

内存信息

3、闭包捕获多个变量时赋值给一个变量
func getFn() -> (Int) -> (Int) {
    var num = 5
    var num1 = 6
    func plus(_ a: Int) -> Int {
        num += a
        num1 += 1
        return num + num1
    }
    
    return plus
}

let plus = getFn()
plus(1)
  • 当把捕获两个变量的闭包赋值给一个变量plus的时候,plus变量也同样会存储两个东西
    • plus总共占用16个字节
    • 前八个字节存储着plus闭包的函数地址
    • 后八个字节存储捕获到的num和num1的信息,并且此信息存储在堆空间
      • 此时的num和num1共同被被包装成一个对象了
      • 其拥有32个字节
      • 第一个八字节存储着类似isa的东西
      • 第二个八字节存储着引用计数
      • 第三个八字节存储的是被包装成对象的num信息
        • 拥有24个字节
        • 前八个字节存储着类似isa的东西
        • 最后八个字节存储的就是num
      • 第三个八字节存储的是被包装成对象的num1信息
        • 拥有24个字节
        • 前八个字节存储着类似isa的东西
        • 最后八个字节存储的就是num1
  • 当在调用funcVar(1)的时候,会找到其存储的前八个字节,也就是闭包的函数地址,进行函数调用
  • 调用的时候,会传入两个参数
    • 一个是1
    • 另一个就是捕获的堆空间的num

内存信息

4、闭包捕获多个值,并且返回多个函数
func getFn() -> ((Int) -> (Int), (Int) -> (Int)) {
    var num = 5
    var num1 = 6
    func plus(_ a: Int) -> Int {
        num += a
        num1 += 1
        return num + num1
    }
    func minus(_ a: Int) -> Int {
        num += a
        num1 += 1
        return num - num1
    }
    
    return (plus, minus)
}

let (plus, minus) = getFn()
plus(1)
plus(2)
minus(1)
minus(2)
  • 这种情况就跟上面那种情况是一样的。可以理解成每返回一个闭包就是有一个上面那种情况的内存信息
  • 因此这里返回两个闭包,就是有两个上面一样的内存信息
  • 只是他们指向的num对象信息相当于是一样的

内存信息1
内存信息2

5、闭包捕获class对象
func getObjFn() -> (Int) -> (Int) {
    var num = 5
    var obj = FnObj()
    obj.a = 3
    func plus(_ a: Int) -> Int {
        num += a
        obj.a += 1
        return num + obj.a
    }
    
    return plus
}

let plus = getObjFn()
plus(1)
  • 对变量的捕获方式跟捕获普通变量几乎一样
  • 只是捕获class对象的时候,不会再次对class对象进行包装
  • 因为要放在堆上面,因此普通变量会包装成class对象
  • class对象本身就是class对象,因此不需要再次包装
  • 因此其实对class对象的捕获相对来说更简单
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Swift 中,闭包是一种自包含的函数代码块,可以在代码中被传递和使用。闭包有能力捕获和存储其所在上下文中任意常量和变量的引用。当闭包中引用了外部的对象时,为了避免循环引用(strong reference cycle),我们需要使用弱引用(weak reference)。 在闭包中使用弱引用可以防止闭包持有外部对象的强引用,从而避免循环引用的问题。循环引用指的是两个或多个对象之间相互持有对方的强引用,导致它们无法被释放,造成内存泄漏。 在 Swift 中,我们可以使用 `[weak self]` 或者 `[unowned self]` 语法来声明一个对外部对象的弱引用。`[weak self]` 会将对外部对象的引用标记为弱引用,而 `[unowned self]` 则标记为无主引用。 下面是一个使用弱引用的闭包示例: ```swift class SomeClass { var completionHandler: (() -> Void)? func doSomething() { completionHandler = { [weak self] in // 使用 weak self 避免循环引用 guard let self = self else { return } // 在闭包中使用 self print("Doing something in \(self)") } completionHandler?() } } let instance = SomeClass() instance.doSomething() ``` 在上面的示例中,我们使用了 `[weak self]` 来声明对外部对象的弱引用。在闭包中,我们使用了 `guard let self = self else { return }` 来确保在闭包执行时,外部对象仍然存在。这样可以避免在闭包中使用已经被释放的对象。 需要注意的是,使用弱引用可能会导致外部对象在闭包执行期间被释放,因此在闭包中使用弱引用的对象时,需要进行额外的判断以确保其存在。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值