前言
Swift 函数非常灵活,它支持函数重载,也可以作为参数被传递,那么它的本质是什么?
一、函数的本质
1. 案例代码
这里我们直接添加:
import Foundation
func add(_ a: Int, _ b: Int) -> Int {
a + b
}
func add(_ a: Double, _ b: Double) -> Double {
a + b
}
/// 这里 a 的参数类型和返回值类型必须标明,否则编译报错
var a: (Int, Int) -> Int = add
print(a(2, 3))
var b = a
print(b(4, 5))
这里我们可以断点调试一下,看看a和b存的是什么:
调试之后可以发现a和b存的是同一个地址,这个地址是函数的metadata,我们可以去源码中查看一下。
2. 源码分析
源码中搜索TargetFunctionTypeMetadata:
这里可以看到TargetFunctionTypeMetadata除了继承了TargetMetadata的相关属性,自身还有掩码Flags和返回类型ResultType。这个Flags我们可以看一下:
查看Flags我们可以看到一些参数相关掩码信息,比如参数数量、Throw、Escaping关键字等。回到TargetFunctionTypeMetadata,我们还可以看到getParameters(获取参数)方法,从该方法的实现可以看出参数在内存中是一段连续的空间。所以参照源码,我们可以得出一段伪代码。
3. 伪代码
通过伪代码来还原方法,这里我们可以获取方法的参数个数:
import Foundation
struct TargetFunctionTypeMetadata {
var kind: Int
var flags: Int
var arguments: ArgumentsBuffer<Any.Type>
func numberArguments() -> Int {
self.flags & 0x0000FFFF
}
}
struct ArgumentsBuffer<Element> {
var element: Element
mutating func buffer(n: Int) -> UnsafeBufferPointer<Element> {
withUnsafePointer(to: &self) {
let ptr = $0.withMemoryRebound(to: Element.self, capacity: 1) { start in
start
}
return UnsafeBufferPointer(start: ptr, count: n)
}
}
mutating func index(of i: Int) -> UnsafeMutablePointer<Element> {
withUnsafePointer(to: &self) {
UnsafeMutablePointer(mutating: UnsafeRawPointer($0).assumingMemoryBound(to: Element.self).advanced(by: i))
}
}
}
func add(_ a: Int, _ b: Int) -> Int {
a + b
}
let value = type(of: add)
let functionType = unsafeBitCast(value as Any.Type, to: UnsafeMutablePointer<TargetFunctionTypeMetadata>.self)
print(functionType.pointee.numberArguments())
/* 执行结果
2
Program ended with exit code: 0
*/
执行后可以打印出add方法的参数个数。
二、总结
函数其实是引用类型,当函数被传递时其实传递的是函数地址。函数也有metadata,通过源码分析我们可以还原出参数相关信息,这些参数在内存中是连续存储的。