You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Swift中实现方法Hook(Swizzle)?有无简便实现方式?

Swift 中的方法 Hook(Method Swizzling)实现指南

Hey there! Let's break down how to implement method swizzling (aka method hooking) in Swift, plus share some streamlined approaches to make this process easier.

首先,先搞懂什么是 Method Swizzling?

Method swizzling is a technique from the Objective-C runtime that lets you swap the implementations of two methods. In Swift, we can leverage this too—but only for classes that inherit from NSObject, since it relies on the Objective-C runtime system. It's super useful for adding custom logic to existing methods without modifying the original class code (like tracking view controller lifecycle events, or adding logging to system methods).

基础实现步骤

Let's walk through a concrete example: hooking viewDidLoad on UIViewController.

Step 1: Prepare your extension and swizzle trigger

We'll use a static stored property to ensure our swizzle code runs exactly once (this is thread-safe since static let initializers in Swift are guaranteed to be atomic).

import UIKit

extension UIViewController {
    // 这个静态常量的闭包会在第一次访问时执行,且只执行一次
    private static let swizzleToken: Void = {
        // 1. 定义原方法和替换方法的选择器
        let originalSelector = #selector(viewDidLoad)
        let swizzledSelector = #selector(swizzled_viewDidLoad)
        
        // 2. 获取方法指针
        guard let originalMethod = class_getInstanceMethod(self, originalSelector),
              let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else {
            assertionFailure("Failed to retrieve methods for swizzling!")
            return
        }
        
        // 3. 处理原方法未在当前类实现的情况(比如UIViewController的viewDidLoad默认是空实现,子类重写)
        let didAddOriginalMethod = class_addMethod(
            self,
            originalSelector,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod)
        )
        
        if didAddOriginalMethod {
            // 如果原方法没在当前类实现,先把原方法的实现设为我们的swizzled方法,再把swizzled方法的实现替换成原方法
            class_replaceMethod(
                self,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod)
            )
        } else {
            // 如果原方法已经存在,直接交换两个方法的实现
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }()
    
    // 4. 我们的替换方法
    @objc private func swizzled_viewDidLoad() {
        // 注意:这里调用swizzled_viewDidLoad()其实是调用原来的viewDidLoad(因为实现已经交换了)
        swizzled_viewDidLoad()
        
        // 这里添加自定义逻辑
        print("✅ Hooked viewDidLoad for \(type(of: self)): \(self)")
    }
    
    // 对外暴露的触发方法(也可以在App启动时直接访问swizzleToken)
    static func enableViewDidLoadHook() {
        _ = swizzleToken
    }
}

Step 2: Trigger the swizzle

Call UIViewController.enableViewDidLoadHook() somewhere early in your app (like application(_:didFinishLaunchingWithOptions:) in AppDelegate), and every view controller's viewDidLoad will now run your custom logic after the original implementation.

更简便的实现方式

Writing the full swizzle logic every time is tedious. Let's wrap it in a reusable helper extension to avoid repeating boilerplate code:

import ObjectiveC
import Foundation

extension NSObject {
    /// 通用的实例方法Swizzle工具函数
    static func swizzleInstanceMethod(originalSelector: Selector, swizzledSelector: Selector) {
        guard let originalMethod = class_getInstanceMethod(self, originalSelector),
              let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else {
            assertionFailure("❌ Failed to swizzle method: \(originalSelector)")
            return
        }
        
        let didAddOriginalMethod = class_addMethod(
            self,
            originalSelector,
            method_getImplementation(swizzledMethod),
            method_getTypeEncoding(swizzledMethod)
        )
        
        if didAddOriginalMethod {
            class_replaceMethod(
                self,
                swizzledSelector,
                method_getImplementation(originalMethod),
                method_getTypeEncoding(originalMethod)
            )
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod)
        }
    }
}

Now using it is a breeze! For example, hooking a custom class's method:

class MyService: NSObject {
    // 注意:Swift类的方法要加dynamic才能被Swizzle(因为Swift默认会优化非dynamic方法,不暴露给runtime)
    dynamic func fetchData() {
        print("Original fetchData implementation")
    }
}

extension MyService {
    private static let swizzleToken: Void = {
        swizzleInstanceMethod(originalSelector: #selector(fetchData), swizzledSelector: #selector(swizzled_fetchData))
    }()
    
    @objc private func swizzled_fetchData() {
        swizzled_fetchData()
        print("✅ Hooked fetchData with custom logic!")
    }
    
    static func enableFetchDataHook() {
        _ = swizzleToken
    }
}

关键注意事项

  • Only for NSObject subclasses: Swizzling relies on the Objective-C runtime, so your class must inherit from NSObject.
  • Add dynamic to Swift methods: For methods in your own Swift classes, mark them dynamic to ensure they're exposed to the runtime.
  • Run once, only once: Always use a thread-safe way to execute swizzle code once (like the static let trick above). Running it multiple times will swap the implementations back to original.
  • Call the "original" method: In your swizzled method, don't forget to call the swizzled method itself—this executes the original implementation after/before your custom logic (depending on where you place it).

内容的提问来源于stack exchange,提问作者Yanni Wang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 20:28:13