Swift 方法派发机制深度解析

Swift 方法派发机制深度解析

深入理解 Swift 中的静态派发、动态派发与性能优化,对比 Objective-C 消息派发机制

前言

在 Swift 开发中,我们经常会遇到这样的问题:

  • 为什么 final关键字能提升性能?
  • 为什么协议扩展中的方法调用结果和我预期的不一样?
  • 什么时候应该使用 @objc dynamic
  • structclass的性能差异本质是什么?
  • Swift 和 Objective-C 的方法派发有什么本质区别?

这些问题的答案都指向同一个核心概念——方法派发(Method Dispatch)。

本文将深入剖析 Swift 中的方法派发机制,并与 Objective-C 的消息派发机制进行对比,帮助你理解两种语言如何在编译期和运行期确定方法调用,以及如何利用这些知识写出更高效的代码。

目录

  • 什么是方法派发
  • Objective-C 的消息派发机制
  • Swift 中的四种派发方式
  • Swift vs Objective-C 派发机制对比
  • 静态派发详解
  • 动态派发详解
  • 不同类型的派发规则
  • 协议派发的坑
  • Swift 与 OC 混编的派发规则
  • 性能对比与优化建议
  • 实战案例
  • 总结

什么是方法派发

方法派发(Method Dispatch) 是指程序在调用一个方法时,如何确定要执行哪个具体实现的过程。

简单来说:

class Animal {

    func makeSound() { print("Some sound") }

}


class Dog: Animal {

    override func makeSound() { print("Woof!") }

}


let animal: Animal = Dog()

animal.makeSound()  // 输出什么?如何确定调用哪个实现?  

当我们调用 animal.makeSound()时,编译器或运行时需要决定:

  • 是调用 Animal的实现?
  • 还是调用 Dog的实现?

这个决策过程就是方法派发。

派发时机分类

Objective-C 的消息派发机制

OC 的核心:消息传递

Objective-C 的方法调用本质上是消息传递(Message Passing):

// Objective-C
[receiver message];

// 本质上编译为:
objc_msgSend(receiver, @selector(message));  
消息派发的完整流程

当调用 [obj method] 时,runtime 执行以下步骤:

1. 缓存查找(Cache Lookup)  
   ↓ 未命中
2. 方法列表查找(Method List)  
   ↓ 未找到
3. 父类方法列表查找(Superclass Method List)  
   ↓ 未找到
4. 消息转发(Message Forwarding)  
   ├─ 动态方法解析(resolveInstanceMethod:)
   ├─ 快速转发(forwardingTargetForSelector:)
   └─ 完整转发(forwardInvocation:)
   ↓ 仍未处理
5. 抛出异常:unrecognized selector sent to instance  
详细步骤解析
1. 缓存查找(最快路径)
// 每个类都有一个方法缓存(Cache)

struct objc_cache {

    unsigned int mask;

    unsigned int occupied;

    Method buckets[1];  // 哈希表

};


// 查找过程(伪代码)

cache_entry = cache[selector & mask];

if (cache_entry->selector == selector) {

    return cache_entry->implementation;  // 缓存命中

}
特点:
  • 使用哈希表存储最近调用的方法

  • 命中率高时性能接近直接派发

  • 首次调用或缓存未命中时需要完整查找

2. 方法列表查找
// 类的方法列表结构

struct objc_class {

    Class isa;

    Class superclass;

    cache_t cache;           // 方法缓存

    class_data_bits_t bits;  // 包含方法列表

};


// 查找过程

for (method in class->methodList) {

    if (method->selector == target_selector) {

        // 找到了,加入缓存

        cache_insert(class->cache, method);

        return method->implementation;

    }

}
3. 父类链查找
// 递归查找父类

Class currentClass = class;

while (currentClass != nil) {

    if (method = findMethodInClass(currentClass, selector)) {

        cache_insert(originalClass->cache, method);

        return method->implementation;

    }

    currentClass = currentClass->superclass;

}
4. 消息转发机制
// 第一步:动态方法解析

+ (BOOL)resolveInstanceMethod:(SEL)sel {

    if (sel == @selector(dynamicMethod)) {

        // 动态添加方法实现

        class_addMethod([self class], sel, 

                       (IMP)dynamicMethodIMP, "v@:");

        return YES;

    }

    return [super resolveInstanceMethod:sel];

}


// 第二步:快速转发

- (id)forwardingTargetForSelector:(SEL)aSelector {

    if (aSelector == @selector(someMethod)) {

        return alternateObject;  // 转发给其他对象

    }

    return [super forwardingTargetForSelector:aSelector];

}


// 第三步:完整转发

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {

    return [NSMethodSignature signatureWithObjCTypes:"v@:"];

}


- (void)forwardInvocation:(NSInvocation *)anInvocation {

    [anInvocation invokeWithTarget:otherObject];

}
OC 消息派发的特点
优点:
1.极致的动态性
  • 运行时添加/替换方法(Method Swizzling)
  • 消息转发机制
  • 动态类型(id 类型)
// Method Swizzling 示例

Method originalMethod = class_getInstanceMethod([self class], 

                                               @selector(original));

Method swizzledMethod = class_getInstanceMethod([self class], 

                                               @selector(swizzled));

method_exchangeImplementations(originalMethod, swizzledMethod);  
2.强大的运行时能力
  • KVO(Key-Value Observing)
  • KVC(Key-Value Coding)
  • Runtime 反射
// KVO 的实现依赖于消息派发

[object addObserver:self 

         forKeyPath:@"property" 

            options:NSKeyValueObservingOptionNew 

            context:nil];

缺点:

1.性能开销大
  • 每次方法调用都要查找
  • 即使缓存命中,仍有少量哈希计算开销
  • 无法被编译器优化(内联等)
2.类型安全性差
  • 编译期无法检查方法是否存在
  • 容易出现运行时崩溃
id obj = @"Hello";

[obj nonExistentMethod];  // 编译通过,运行时崩溃
OC 消息派发的性能
// 性能测试(伪代码)

// 直接调用(C 函数):    1x

// OC 方法(缓存命中):    1.5x

// OC 方法(缓存未命中):  3-5x

// 消息转发:              10-20x

Swift 中的四种派发方式

与 Objective-C 不同,Swift 支持多种派发方式,根据场景选择最优方案。

1. 直接派发(Direct Dispatch / Static Dispatch)
  • 特点:编译期确定调用地址,直接跳转

  • 性能:最快,可被编译器内联优化

  • 使用场景:值类型方法、final方法、private方法

struct Point {

    var x: Double

    var y: Double


    // 直接派发,编译为直接的函数调用

    func distance() -> Double {

        return sqrt(x * x + y * y)

    }

}


// 编译后类似于:

// call Point.distance(Point)  // 直接调用
2. 函数表派发(Table Dispatch / V-Table)
  • 特点:通过虚函数表(V-Table)查找方法实现

  • 性能:较快,一次数组查找

  • 使用场景:类的实例方法(非 final)

class Animal {

    // 通过 V-Table 派发

    func makeSound() { 

        print("Some sound") 

    }

}


class Dog: Animal {

    // V-Table 中存储了 Dog 的实现地址

    override func makeSound() { 

        print("Woof!") 

    }

}


// 调用过程:

let animal: Animal = Dog()

animal.makeSound()

// 1. 获取对象的 V-Table 指针

// 2. 在 V-Table 中查找 makeSound 的索引

// 3. 调用对应的实现
V-Table 结构示意:
Animal V-Table:

[0] makeSound -> Animal.makeSound 地址


Dog V-Table:

[0] makeSound -> Dog.makeSound 地址  // 覆盖了父类的实现
3. 见证表派发(Witness Table Dispatch)
  • 特点:通过见证表(Witness Table)查找协议方法实现

  • 性能:与 V-Table 类似

  • 使用场景:协议类型的方法调用

protocol Drawable {

    func draw()

}


struct Circle: Drawable {

    func draw() { print("Drawing circle") }

}


struct Rectangle: Drawable {

    func draw() { print("Drawing rectangle") }

}


let shape: Drawable = Circle()

shape.draw()  // 通过 Witness Table 派发  
Witness Table 结构:
Circle Witness Table for Drawable:

[0] draw -> Circle.draw


Rectangle Witness Table for Drawable:

[0] draw -> Rectangle.draw
4. 消息派发(Message Dispatch)
  • 特点:使用 Objective-C 的消息转发机制

  • 性能:最慢,需要运行时查找

  • 使用场景:@objc dynamic方法、与OC交互

class MyClass: NSObject {

    @objc dynamic func dynamicMethod() {

        print("Can be swizzled")

    }

}


// 本质上编译为:

// objc_msgSend(obj, selector("dynamicMethod"))

Swift vs Objective-C 派发机制对比

根本区别

详细对比
1. 方法调用开销
// Swift - 静态派发(Struct)

struct SwiftStruct {

    func method() { }

}

let s = SwiftStruct()

s.method()

// 编译为:call SwiftStruct.method(SwiftStruct)

// 开销:几乎为 0,可被内联


// Swift - V-Table 派发(Class)

class SwiftClass {

    func method() { }

}

let c = SwiftClass()

c.method()

// 编译为:

// 1. load vtable_ptr from c

// 2. load method_ptr from vtable[index]

// 3. call method_ptr(c)

// 开销:2 次内存访问 + 1 次间接调用
// Objective-C - 消息派发

@interface ObjCClass : NSObject

- (void)method;

@end


ObjCClass *obj = [[ObjCClass alloc] init];

[obj method];

// 编译为:objc_msgSend(obj, @selector(method))

// 开销:

// 1. 缓存查找(1-2 次内存访问)

// 2. 如果缓存未命中:方法列表查找(N 次比较)

// 3. 如果找不到:父类链查找 + 消息转发

// 总开销:3-10+ 次内存访问和比较
2. 类型系统
// Swift - 强类型

let string: String = "Hello"

string.uppercased()  // ✅ 编译期确定方法存在


// string.nonExistentMethod()  // ❌ 编译错误


// 协议约束

func process<T: Drawable>(_ item: T) {

    item.draw()  // 编译期保证 T 实现了 draw

}
// Objective-C - 弱类型

id obj = @"Hello";

[obj uppercaseString];  // ✅ 运行时查找


[obj nonExistentMethod];  // ⚠️ 编译警告,运行时崩溃


// 动态类型

- (void)processObject:(id)obj {

    [obj someMethod];  // 编译期无法确定方法是否存在

}
3. 继承和重写
// Swift - 明确的重写语义

class Base {

    func method() { print("Base") }

}


class Derived: Base {

    override func method() { print("Derived") }

    // 必须使用 override 关键字

}


// 可以禁止重写

final class FinalClass {

    func method() { }  // 无法被继承

}


class AnotherBase {

    final func finalMethod() { }  // 无法被重写

}
// Objective-C - 隐式的重写

@interface Base : NSObject

- (void)method;

@end


@interface Derived : Base

- (void)method;  // 自动重写,无需关键字

@end


// 无法禁止重写(没有 final)
4. 运行时能力
// Objective-C - 强大的运行时

// Method Swizzling

Method original = class_getInstanceMethod([UIViewController class], 

                                          @selector(viewWillAppear:));

Method swizzled = class_getInstanceMethod([UIViewController class], 

                                          @selector(xxx_viewWillAppear:));

method_exchangeImplementations(original, swizzled);


// 动态添加方法

class_addMethod([MyClass class], 

               @selector(dynamicMethod), 

               (IMP)dynamicMethodIMP, 

               "v@:");


// KVO

[object addObserver:self 

         forKeyPath:@"property" 

            options:0 

            context:nil];
// Swift - 有限的运行时能力

class MyClass: NSObject {

    // 必须标记 @objc dynamic 才能使用 OC Runtime 特性

    @objc dynamic func swizzlableMethod() { }


    // 普通 Swift 方法无法 swizzle

    func normalMethod() { }

}


// KVO 需要 @objc dynamic

class Observable: NSObject {

    @objc dynamic var property: String = ""

}


// Swift 不支持动态添加方法

// 但有自己的反射机制(Mirror)

let mirror = Mirror(reflecting: obj)

for child in mirror.children {

    print(child.label, child.value)

}
5. 扩展(Extension)的行为差异
// Objective-C - Category 方法使用消息派发

@interface NSString (MyExtension)

- (void)myMethod;

@end


@implementation NSString (MyExtension)

- (void)myMethod {

    NSLog(@"Extension method");

}

@end


NSString *str = @"Hello";

[str myMethod];  // 通过消息派发查找
// Swift - Extension 方法使用静态派发

extension String {

    func myMethod() {

        print("Extension method")

    }

}


let str = "Hello"

str.myMethod()  // 静态派发,编译期确定


// 无法重写 extension 方法

class MyString: NSString {

    // ❌ 无法重写 Swift extension 方法

}
性能对比测试
// 测试代码(调用 1000 万次)

class TestClass {

    func normalMethod() -> Int { return 1 }

    final func finalMethod() -> Int { return 1 }

    @objc dynamic func dynamicMethod() -> Int { return 1 }

}


// 结果:

// Swift struct method:       10ms   (静态派发)

// Swift final method:        10ms   (静态派发)

// Swift class method:        20ms   (V-Table 派发)

// Swift @objc dynamic:       80ms   (OC 消息派发)

// OC instance method:        85ms   (OC 消息派发)
性能差距:
  • 静态派发 vs OC 消息派发:8-9 倍
  • V-Table vs OC 消息派发:4 倍

何时选择哪种机制?

选择 Swift 原生机制(静态/表派发)

✅ 适合场景:

  • 性能敏感的代码(游戏、图形处理)
  • 不需要运行时动态性
  • 新的 Swift 项目
  • 类型安全要求高
// 示例:游戏实体系统

struct Entity {

    var position: Vector2D

    var velocity: Vector2D


    mutating func update(deltaTime: Float) {

        position.x += velocity.x * deltaTime

        position.y += velocity.y * deltaTime

    }

}


// 静态派发,可被完全内联优化

func updateEntities(_ entities: inout [Entity], deltaTime: Float) {

    for i in 0..<entities.count {

        entities[i].update(deltaTime: deltaTime)

    }

}
选择 OC 消息派发(@objc dynamic)

✅ 适合场景:

  • 需要 Method Swizzling
  • 需要 KVO
  • 与 OC 代码交互
  • 需要运行时动态添加方法
// 示例:埋点框架

class AnalyticsTracker: NSObject {

    @objc dynamic func trackEvent(_ name: String) {

        // 原始实现

        sendToServer(name)

    }

}


// 在测试中 swizzle

extension AnalyticsTracker {

    @objc dynamic func test_trackEvent(_ name: String) {

        print("Test: \(name)")

        // 不发送到服务器

    }

}

静态派发详解

什么时候使用静态派发?

Swift 编译器会在以下场景使用静态派发:

1. 值类型(Struct、Enum)
struct Calculator {

    func add(_ a: Int, _ b: Int) -> Int {

        return a + b  // 静态派发

    }

}


// 编译后:

// call Calculator.add(Calculator, Int, Int) -> Int

原因:值类型不支持继承,编译器知道具体类型。

2. final 类和方法
final class FinalClass {

    func method() { }  // 静态派发

}


class BaseClass {

    final func finalMethod() { }  // 静态派发

    func normalMethod() { }       // 表派发

}

原因:final保证不会被继承或重写。

3. private 和 fileprivate 方法
class MyClass {

    private func privateMethod() {  // 静态派发

        print("Private")

    }


    func publicMethod() {  // 表派发

        print("Public")

    }

}

原因:编译器能确定当前模块内没有重写。

4. 扩展(Extension)中的方法
class MyClass { }


extension MyClass {

    func extensionMethod() {  // 静态派发

        print("Extension")

    }

}


// extension 方法不会加入 V-Table,无法被重写

class SubClass: MyClass {

    // ❌ 无法重写 extensionMethod

}
5. 全模块优化(Whole Module Optimization)

开启 WMO 后,编译器可以分析整个模块,将更多方法静态化。

// 在 WMO 下,如果编译器确认没有子类重写,可能会静态派发

internal class InternalClass {

    func method() { }

}

静态派发的优势


1.性能更高:无需查表,可被内联优化


2.编译期优化:死代码消除、常量折叠


3.代码体积更小:内联后减少函数调用开销

动态派发详解

V-Table 派发原理

每个类都有一个虚函数表(V-Table),存储方法的实现地址:

class Animal {

    func eat() { print("Animal eating") }

    func sleep() { print("Animal sleeping") }

}


class Dog: Animal {

    override func eat() { print("Dog eating") }

    func bark() { print("Woof!") }

}
V-Table 结构:
Animal V-Table:

[0] eat   -> Animal.eat

[1] sleep -> Animal.sleep


Dog V-Table:

[0] eat   -> Dog.eat       // 重写

[1] sleep -> Animal.sleep  // 继承

[2] bark  -> Dog.bark      // 新增
调用过程:
let animal: Animal = Dog()

animal.eat()


// 汇编级别的步骤:

// 1. mov rax, [animal]        ; 获取对象指针

// 2. mov rax, [rax]           ; 获取 V-Table 指针

// 3. mov rax, [rax + 0*8]     ; 获取 eat 方法指针(索引 0)

// 4. call rax                 ; 调用方法
Witness Table 派发原理

协议使用见证表(Witness Table)存储协议要求的实现:

protocol Drawable {

    func draw()

}


struct Circle: Drawable {

    func draw() { print("Circle") }

}


struct Rectangle: Drawable {

    func draw() { print("Rectangle") }

}
Witness Table:
Circle Witness Table for Drawable:

[0] draw -> Circle.draw


Rectangle Witness Table for Drawable:

[0] draw -> Rectangle.draw
存在容器(Existential Container):
let shape: Drawable = Circle()

// shape 实际上是一个 Existential Container:

// struct ExistentialContainer {

//     var valueBuffer: (Int, Int, Int)  // 存储值(小对象)或指针(大对象)

//     var type: Metadata                 // 类型元数据

//     var witnessTable: WitnessTable     // 见证表指针

// }
消息派发原理(@objc dynamic)

使用 Objective-C 运行时的消息转发机制:

@objc dynamic func dynamicMethod() { }


// 编译为:

objc_msgSend(obj, selector("dynamicMethod"))  

特性:

  • 支持方法交换(Method Swizzling)
  • 支持 KVO
  • 支持消息转发
  • 性能最差

不同类型的派发规则

Class 类的派发规则

class MyClass {

    func normalMethod() { }           // V-Table 派发

    final func finalMethod() { }      // 静态派发

    private func privateMethod() { }  // 静态派发

    @objc func objcMethod() { }       // V-Table 派发(可被 OC 调用)

    @objc dynamic func dynamicMethod() { } // 消息派发

}


extension MyClass {

    func extensionMethod() { }  // 静态派发(无法被重写)

}
Struct/Enum 的派发规则
所有方法都是静态派发
struct MyStruct {

    func method() { }  // 静态派发

}


enum MyEnum {

    case a, b

    func method() { }  // 静态派发

}
Protocol 的派发规则

协议派发的坑

这是 Swift 中最容易出错的地方!

案例 1:协议扩展方法不会被重写
protocol Animal {

    func requiredMethod()

}


extension Animal {

    func extensionMethod() {

        print("From protocol extension")

    }

}


struct Dog: Animal {

    func requiredMethod() {

        print("Dog implementation")

    }


    func extensionMethod() {

        print("Dog extension method")

    }

}


let dog = Dog()

dog.extensionMethod()  // 输出:Dog extension method


let animal: Animal = Dog()

animal.extensionMethod()  // 输出:From protocol extension ⚠️  

原因:

  • extensionMethod不是协议要求,使用静态派发
  • 以协议类型调用时,编译器在编译期就确定了调用 extension的实现

案例 2:协议要求 vs 协议扩展

protocol Drawable {

    func draw()  // 协议要求

}


extension Drawable {

    func draw() {

        print("Default draw")  // 默认实现

    }


    func debugInfo() {

        print("Debug info")  // 非协议要求

    }

}


struct Circle: Drawable {

    func draw() {

        print("Circle draw")

    }


    func debugInfo() {

        print("Circle debug")

    }

}


let shape: Drawable = Circle()

shape.draw()       // 输出:Circle draw (动态派发)

shape.debugInfo()  // 输出:Debug info (静态派发) ⚠️  
如何避免?

方法 1:在协议中声明所有需要被重写的方法

protocol Drawable {

    func draw()

    func debugInfo()  // 在协议中声明

}


extension Drawable {

    func draw() { print("Default") }

    func debugInfo() { print("Default debug") }

}


struct Circle: Drawable {

    func draw() { print("Circle") }

    func debugInfo() { print("Circle debug") }

}


let shape: Drawable = Circle()

shape.debugInfo()  // 输出:Circle debug ✅  

方法 2:使用具体类型而非协议类型

let circle = Circle()  // 具体类型,非协议类型

circle.debugInfo()  // 输出:Circle debug  

Swift 与 OC 混编的派发规则

NSObject 子类
// 继承自 NSObject 的 Swift 类

class MyViewController: UIViewController {

    // 1. 普通方法:V-Table 派发(Swift 侧)

    func swiftMethod() {

        print("Swift method")

    }


    // 2. override OC 方法:使用 OC 的消息派发

    override func viewDidLoad() {

        super.viewDidLoad()

    }


    // 3. @objc 标记:可被 OC 调用,但仍是 V-Table 派发

    @objc func objcMethod() {

        print("Can be called from OC")

    }


    // 4. @objc dynamic:使用 OC 消息派发

    @objc dynamic func dynamicMethod() {

        print("OC message dispatch")

    }

}
@objc vs @objc dynamic
class MyClass: NSObject {

    // @objc: 暴露给 OC,但仍用 V-Table 派发

    @objc func method1() { }


    // @objc dynamic: 使用 OC 消息派发

    @objc dynamic func method2() { }

}


// 从 Swift 调用

let obj = MyClass()

obj.method1()  // V-Table 派发

obj.method2()  // 消息派发


// 从 OC 调用

MyClass *obj = [[MyClass alloc] init];

[obj method1];  // 消息派发

[obj method2];  // 消息派发
规则总结:
  • @objc:允许 OC 调用,Swift 内部仍用 V-Table
  • @objc dynamic:Swift 和 OC 都用消息派发
  • 继承 NSObject不会自动改变派发方式
实战场景:Method Swizzling
// ❌ 无法 Swizzle 普通 Swift 方法

class SwiftClass {

    func method() { }  // V-Table,无法 swizzle

}


// ✅ 必须使用 @objc dynamic

class SwiftClass: NSObject {

    @objc dynamic func method() { }  // 可以 swizzle

}


// Swizzling 代码

extension SwiftClass {

    @objc dynamic func swizzled_method() {

        print("Swizzled")

        swizzled_method()  // 调用原始实现

    }


    static func swizzle() {

        let original = #selector(method)

        let swizzled = #selector(swizzled_method)


        guard let originalMethod = class_getInstanceMethod(self, original),

              let swizzledMethod = class_getInstanceMethod(self, swizzled) else {

            return

        }


        method_exchangeImplementations(originalMethod, swizzledMethod)

    }

}

性能对比与优化建议

性能测试
// 测试代码(简化)

class TestClass {

    func normalMethod() -> Int { return 1 }

    final func finalMethod() -> Int { return 1 }

    @objc dynamic func dynamicMethod() -> Int { return 1 }

}


struct TestStruct {

    func method() -> Int { return 1 }

}


// 调用 1000 万次的性能对比:

// Struct method:        10ms  (静态派发)

// Class final method:   10ms  (静态派发)

// Class normal method:  20ms  (V-Table 派发)

// Dynamic method:       80ms  (消息派发)

// OC method:            85ms  (消息派发)
性能倍数:
  • 静态派发:1x(基准,最快)
  • V-Table:2x
  • 消息派发:8x
优化建议
1. 优先使用值类型
// ❌ 不必要的 class

class Point {

    var x: Double

    var y: Double

}


// ✅ 使用 struct

struct Point {

    var x: Double

    var y: Double

}
原因:
  • Struct 使用静态派发
  • 无需堆分配
  • 无引用计数开销
2. 使用 final 关键字
// ❌ 未优化

class ViewManager {

    func updateView() { }

}


// ✅ 使用 final(如果不需要继承)

final class ViewManager {

    func updateView() { }

}


// ✅ 或者只 final 部分方法

class ViewManager {

    final func updateView() { }  // 高频调用的方法

    func configure() { }         // 可能需要重写的方法

}
3. 使用 private/fileprivate
class MyClass {

    // ✅ 内部方法标记为 private

    private func helperMethod() { }


    public func publicMethod() {

        helperMethod()

    }

}
4. 避免不必要的 @objc dynamic
// ❌ 不需要动态特性却使用了 dynamic

class MyClass {

    @objc dynamic func method() { }

}


// ✅ 只在需要时使用

class MyClass {

    func method() { }  // 普通方法就够了


    @objc dynamic func needsSwizzling() { }  // 确实需要的情况

}
5. 使用泛型代替协议类型
// ❌ 使用协议类型(Witness Table 派发)

func process(items: [Drawable]) {

    for item in items {

        item.draw()  // Witness Table 派发

    }

}


// ✅ 使用泛型(静态派发 + 特化)

func process<T: Drawable>(items: [T]) {

    for item in items {

        item.draw()  // 可被静态派发和内联

    }

}
6. 开启 Whole Module Optimization

在 Build Settings 中开启:

  • Optimization Level:-O(Release) 
  • Whole Module Optimization:Yes

效果:

  • 编译器可以分析整个模块
  • 将更多方法从动态派发优化为静态派发
  • 更激进的内联优化

实战案例

案例 1:优化高性能计算代码
// ❌ 优化前:使用协议和 class

protocol MathOperation {

    func calculate(_ value: Double) -> Double

}


class SquareOperation: MathOperation {

    func calculate(_ value: Double) -> Double {

        return value * value

    }

}


func processBatch(_ values: [Double], operation: MathOperation) -> [Double] {

    return values.map { operation.calculate($0) }  // Witness Table 派发(动态)

}


// ✅ 优化后:使用泛型 + struct

protocol MathOperation {

    func calculate(_ value: Double) -> Double

}


struct SquareOperation: MathOperation {

    func calculate(_ value: Double) -> Double {

        return value * value

    }

}


func processBatch<Op: MathOperation>(_ values: [Double], operation: Op) -> [Double] {

    return values.map { operation.calculate($0) }  // 静态派发 + 内联

}


// 性能提升:约 3-5 倍
案例 2:修复协议扩展的坑
// ❌ 错误实现

protocol ViewConfigurable {

    func configure()

}


extension ViewConfigurable {

    func configure() {

        setupDefaultStyle()

    }


    func setupDefaultStyle() {

        print("Default style")

    }

}


class CustomView: UIView, ViewConfigurable {

    func setupDefaultStyle() {

        print("Custom style")

    }

}


let view: ViewConfigurable = CustomView()

view.configure()  // 调用 setupDefaultStyle 时输出:Default style ⚠️


// ✅ 正确实现:在协议中声明

protocol ViewConfigurable {

    func configure()

    func setupDefaultStyle()  // 声明为协议要求

}


extension ViewConfigurable {

    func configure() {

        setupDefaultStyle()

    }


    func setupDefaultStyle() {

        print("Default style")

    }

}


class CustomView: UIView, ViewConfigurable {

    func setupDefaultStyle() {

        print("Custom style")

    }

}


let view: ViewConfigurable = CustomView()

view.configure()  // 输出:Custom style ✅  
案例 3:Swift 与 OC 混编优化
// ❌ 过度使用 dynamic

class APIManager: NSObject {

    @objc dynamic func fetchData() { }  // 不需要 dynamic

}


// ✅ 只使用 @objc

class APIManager: NSObject {

    @objc func fetchData() { }  // Swift 内部用 V-Table,OC 可调用

}


// 性能提升:Swift 侧调用快 4 倍
案例 4:游戏实体系统优化
// ❌ 使用 class + 协议

protocol GameEntity {

    var position: CGPoint { get set }

    func update(deltaTime: Float)

}


class Player: GameEntity {

    var position: CGPoint

    func update(deltaTime: Float) { /* ... */ }

}


var entities: [GameEntity] = []  // 存在容器开销 + Witness Table


// ✅ 使用 struct + 泛型

protocol GameEntity {

    var position: CGPoint { get set }

    mutating func update(deltaTime: Float)

}


struct Player: GameEntity {

    var position: CGPoint

    mutating func update(deltaTime: Float) { /* ... */ }

}


// 使用具体类型数组

var players: [Player] = []  // 连续内存 + 静态派发


// 或使用泛型容器

struct EntityManager<T: GameEntity> {

    var entities: [T]


    mutating func update(deltaTime: Float) {

        for i in 0..<entities.count {

            entities[i].update(deltaTime: deltaTime)  // 静态派发 + 内联

        }

    }

}


// 性能提升:5-10 倍(取决于实体数量)

总结

核心要点
Swift 与 OC 的本质区别:
  • OC:所有方法调用默认使用消息派发,极致的动态性,性能开销大
  • Swift:默认使用静态派发和表派发,性能优先,动态性受限
Swift 的四种派发方式:
  • 静态派发:最快,用于值类型、final、private
  • V-Table 派发:较快,用于类的普通方法
  • Witness Table 派发:中等,用于协议方法
  • 消息派发:最慢,用于 @objc dynamic
OC 消息派发机制:
  • 缓存查找 → 方法列表 → 父类链 → 消息转发
  • 强大的动态性:Method Swizzling、KVO、动态添加方法
  • 性能开销:比 Swift 静态派发慢 8 倍以上
协议派发的坑:
  • 协议要求的方法:动态派发
  • 协议扩展的非要求方法:静态派发 * 以协议类型调用时,扩展方法不会被"重写"
Swift 与 OC 混编:
  • @objc:暴露给 OC,Swift 内部仍用 V-Table
  • @objc dynamic:Swift 和 OC 都用消息派发
  • 继承 NSObject 不改变派发方式
性能优化策略

决策树
需要 OC Runtime 特性(Swizzling/KVO)?

├─ 是 → 用 @objc dynamic (消息派发)

└─ 否 → 需要继承?

    ├─ 否 → 用 struct (静态派发)

    └─ 是 → 用 class

        ├─ 需要被重写?

        │   ├─ 否 → 用 final (静态派发)

        │   └─ 是 → 普通方法 (V-Table 派发)

        └─ 需要暴露给 OC?

            └─ 是 → 用 @objc (V-Table,OC 可调用)

附录:底层实现细节

Swift V-Table 的内存布局
class Animal {

    var name: String = ""

    func eat() { }

    func sleep() { }

}


// 内存布局(简化)

struct Animal_Instance {

    HeapObject header;           // 引用计数等

    String name;                 // 存储属性

}


struct Animal_VTable {

    void (*destroy)(Animal*);    // 析构函数

    size_t size;                 // 对象大小

    void (*eat)(Animal*);        // 方法 1

    void (*sleep)(Animal*);      // 方法 2

}
OC 消息派发的汇编实现
; objc_msgSend 的简化实现

_objc_msgSend:

    ; 检查 receiver 是否为 nil

    test    rdi, rdi

    je      LNilReceiver


    ; 获取 isa 指针(类指针)

    mov     rax, [rdi]


    ; 在缓存中查找方法

    mov     r10, [rax + 16]      ; 获取 cache

    and     r11, rsi, [r10]      ; selector & mask

    lea     r12, [r10 + r11*16]  ; cache bucket


LCacheLookup:

    cmp     [r12], rsi           ; 比较 selector

    je      LCacheHit            ; 找到了

    cmp     [r12], 0             ; 检查是否为空

    je      LCacheMiss           ; 缓存未命中

    add     r12, 16              ; 下一个 bucket

    jmp     LCacheLookup


LCacheHit:

    jmp     [r12 + 8]            ; 调用 IMP


LCacheMiss:

    ; 调用慢速查找

    jmp     __objc_msgSend_uncached

作者介绍:

  • 时晓东 高级iOS开发工程师

微鲤技术团队

微鲤技术团队承担了中华万年历、Maybe、蘑菇语音、微鲤游戏高达3亿用户的产品研发工作,并构建了完备的大数据平台、基础研发框架、基础运维设施。践行数据驱动理念,相信技术改变世界。