Swift 中的函数是值类型还是引用类型?为什么?

Nil*_*wal 10 swift

考虑这个函数:

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
Run Code Online (Sandbox Code Playgroud)

在这里,我将此函数分配给另一个变量

var mathFunction: (Int, Int) -> Int = addTwoInts
Run Code Online (Sandbox Code Playgroud)

这里addTwoInts是值类型还是引用类型?为什么?

CPR*_*CPR 20

闭包和函数是引用类型。这在 Swift 语言文档中有说明:闭包


Ham*_*ish 13

我会说函数类型最适合引用类型的定义

将引用类型分配给变量或常量,或者将它们传递给函数时,不会复制引用类型。使用对同一现有实例的引用而不是副本。

但是我不认为这是一个特别有用的区别,IMO 提出函数是值类型的论点也是有效的(从技术上讲,它们由一个指针和一个引用组成,当传递函数值)。

更有用的区别是函数具有引用语义,这意味着可以观察实例之间的某种共享状态。这与作为引用类型不同——值类型可能具有引用语义,而引用类型可能具有值语义。

例如,这是一个具有引用语义的值类型:

var foo = 0
struct S {
  var bar: Int {
    get { return foo }
    nonmutating set { foo = newValue }
  }
}

let s = S()
let s1 = s
s.bar += 1
print(s.bar)  // 1
print(s1.bar) // 1
Run Code Online (Sandbox Code Playgroud)

无论ss1共享全局状态,这是通过突变其观察到的。

这是一个具有值语义的引用类型:

final class C {
  let foo = 0
}

let c = C()
let c1 = c
print(c.foo)  // 0
print(c1.foo) // 0
Run Code Online (Sandbox Code Playgroud)

虽然这两个cc1共享状态,这是无法直接观测,因为共享的状态是不可改变的。

那么,为什么函数有引用语义呢?因为它们可以包含捕获的变量,这是可以观察到的共享可变状态。

例如:

func makeFn() -> () -> Int {
  var foo = 0
  return {
    foo += 1
    return foo
  }
}

let fn = makeFn()
let fn1 = fn
print(fn())  // 1
print(fn1()) // 2
Run Code Online (Sandbox Code Playgroud)