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

Swift 5中Swizzle URLSession的data(for:)方法失败,求解决方案

解决URLSession.data(for:)方法Swizzle失败的问题

报错原因

URLSession.data(for:)是Swift 5.5+引入的原生异步方法,未通过@objc暴露给Objective-C runtime,因此无法用#selector(URLSession.data(for:))获取其选择器,常规基于Objective-C runtime的方法交换(method_exchangeImplementations)无法直接生效。

解决方案

方案一:调试专用(使用Swift Runtime私有API)

仅适合本地调试验证猜想,私有API可能导致App Store审核不通过:

import ObjectiveC.runtime
import Foundation

// 声明Swift Runtime私有函数(需手动引入)
func swift_getMethod(_ klass: AnyClass, _ name: UnsafePointer<CChar>) -> UnsafeMutableRawPointer?
func swift_setMethod(_ klass: AnyClass, _ name: UnsafePointer<CChar>, _ method: UnsafeMutableRawPointer?)

extension URLSession {
    // 自定义替换方法
    private func swizzled_data(for request: URLRequest) async throws -> (Data, URLResponse) {
        // 添加自定义逻辑,比如日志、拦截等
        print("Swizzled data(for:) triggered for request: \(request.url?.absoluteString ?? "")")
        // 调用原方法(交换后,当前方法名指向原实现)
        return try await swizzled_data(for: request)
    }
    
    static func swizzleDataFor() {
        guard let originalMethod = swift_getMethod(Self.self, "data(for:)"),
              let swizzledMethod = swift_getMethod(Self.self, "swizzled_data(for:)") else {
            print("Failed to get method pointers")
            return
        }
        
        // 交换方法实现
        let temp = UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<UnsafeMutableRawPointer>.size, alignment: 1)
        memcpy(temp, originalMethod, MemoryLayout<UnsafeMutableRawPointer>.size)
        memcpy(originalMethod, swizzledMethod, MemoryLayout<UnsafeMutableRawPointer>.size)
        memcpy(swizzledMethod, temp, MemoryLayout<UnsafeMutableRawPointer>.size)
        temp.deallocate()
    }
}

// 在App启动时调用交换
// 例如在AppDelegate.didFinishLaunchingWithOptions或SceneDelegate.scene(_:willConnectTo:options:)中
URLSession.swizzleDataFor()

方案二:稳定替代(Swizzle dataTask(with:completionHandler:))

data(for:)的底层实现大概率依赖dataTask(with:completionHandler:),Swizzle该方法可覆盖包括data(for:)在内的所有请求路径,且基于Objective-C runtime,无私有API风险:

import ObjectiveC.runtime
import Foundation

extension URLSession {
    static func swizzleDataTaskCompletion() {
        let originalSelector = #selector(dataTask(with:completionHandler:) as (URLSession) -> (URLRequest, @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask)
        let swizzledSelector = #selector(swizzled_dataTask(with:completionHandler:))
        
        guard let originalMethod = class_getInstanceMethod(Self.self, originalSelector),
              let swizzledMethod = class_getInstanceMethod(Self.self, swizzledSelector) else {
            return
        }
        
        method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    
    @objc private func swizzled_dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask {
        // 添加自定义逻辑
        print("Swizzled dataTask(completion) triggered for request: \(request.url?.absoluteString ?? "")")
        // 调用原方法
        return swizzled_dataTask(with: request, completionHandler: completionHandler)
    }
}

// 在App启动时调用
URLSession.swizzleDataTaskCompletion()

使用该方案后,调用try await URLSession.shared.data(for:)时会触发Swizzled方法的日志,可验证你的猜想。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 19:20:30