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对象的捕获相对来说更简单

猜你喜欢

转载自blog.csdn.net/qq_20255275/article/details/130702550
今日推荐