13. 函数-func

140 阅读2分钟

前言

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))

这里我们可以断点调试一下,看看ab存的是什么:

image-20220122155032348

调试之后可以发现ab存的是同一个地址,这个地址是函数的metadata,我们可以去源码中查看一下。

2. 源码分析

源码中搜索TargetFunctionTypeMetadata

image-20220122160302506

这里可以看到TargetFunctionTypeMetadata除了继承了TargetMetadata的相关属性,自身还有掩码Flags和返回类型ResultType。这个Flags我们可以看一下:

image-20220122160621821

查看Flags我们可以看到一些参数相关掩码信息,比如参数数量、ThrowEscaping关键字等。回到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,通过源码分析我们可以还原出参数相关信息,这些参数在内存中是连续存储的。