Skip to content

消息转发机制

源:Message Forwarding

本文深入讲解 Objective-C 消息转发机制,展示如何在 Kotlin/Native 中实现动态方法解析、多重代理、惰性初始化和 AOP。消息转发是 ObjC 运行时最强大的特性。

项目背景

消息转发的价值

当对象收到无法响应的消息时,运行时提供三次补救机会:

kotlin
// 1. 动态方法解析
override fun resolveInstanceMethod(sel: COpaquePointer?): Boolean

// 2. 快速转发
override fun forwardingTargetForSelector(aSelector: COpaquePointer?): Any?

// 3. 完整消息转发
override fun methodSignatureForSelector(aSelector: COpaquePointer?): NSMethodSignature?
override fun forwardInvocation(anInvocation: NSInvocation)

完整项目架构

项目结构

MessageForwardingDemo/
├── src/
│   ├── iosMain/kotlin/
│   │   ├── DynamicResolver.kt  # 动态方法解析
│   │   ├── FastForwarder.kt    # 快速转发
│   │   ├── FullForwarder.kt    # 完整转发
│   │   ├── ProxyPatterns.kt    # 代理模式
│   │   └── AOPFramework.kt     # AOP框架
│   └── iosTest/kotlin/
│       └── ForwardingTests.kt
└── build.gradle.kts

Gradle 配置

kotlin
// build.gradle.kts
kotlin {
    iosArm64()
    iosSimulatorArm64()
    
    sourceSets {
        val iosMain by creating {
            dependencies {
                implementation("platform.Foundation:Foundation")
                implementation("platform.UIKit:UIKit")
            }
        }
    }
}

消息转发流程

完整流程图

方法调用

1. 方法缓存查找 → 找到 → 执行
  ↓ 未找到
2. 类的方法列表查找 → 找到 → 执行并缓存
  ↓ 未找到
3. 父类方法查找 → 找到 → 执行并缓存
  ↓ 未找到
4. resolveInstanceMethod/resolveClassMethod → 动态添加 → 重新查找
  ↓ 未处理
5. forwardingTargetForSelector → 返回对象 → 重新发送消息
  ↓ 返回nil
6. methodSignatureForSelector → 返回签名 → forwardInvocation
  ↓ 返回nil
7. doesNotRecognizeSelector → 抛出异常

动态方法解析

DynamicResolver - 动态方法解析

kotlin
// src/iosMain/kotlin/DynamicResolver.kt
@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)

import kotlinx.cinterop.*
import platform.Foundation.*
import platform.objc.*

/**
 * 动态方法解析器
 */
@ObjCClass
class DynamicResolver : NSObject() {
    
    private val dynamicMethods = mutableMapOf<String, CPointer<CFunction<*>>>()
    
    /**
     * 注册动态方法
     */
    fun registerMethod(
        selectorName: String,
        implementation: CPointer<CFunction<*>>,
        typeEncoding: String = "v@:"
    ) {
        dynamicMethods[selectorName] = implementation
    }
    
    @ObjCMethod
    override fun resolveInstanceMethod(sel: COpaquePointer?): Boolean {
        val selectorName = sel?.let { sel_getName(it)?.toKString() } ?: ""
        
        val implementation = dynamicMethods[selectorName]
        if (implementation != null) {
            class_addMethod(
                object_getClass(this),
                sel,
                implementation.reinterpret(),
                "v@:"
            )
            
            println("✅ 动态添加方法: $selectorName")
            return true
        }
        
        return super.resolveInstanceMethod(sel)
    }
}

// 使用示例
fun dynamicResolverExample() {
    val resolver = DynamicResolver()
    
    // 定义方法实现
    val methodImpl = staticCFunction { 
        self: COpaquePointer?, _cmd: COpaquePointer? ->
        println("动态方法被调用!")
    }
    
    // 注册方法
    resolver.registerMethod("customMethod", methodImpl)
    
    // 调用(第一次会触发解析)
    RuntimeOps.sendMessage(resolver, "customMethod")
}

属性访问器自动生成

kotlin
/**
 * 自动生成getter/setter
 */
@ObjCClass
class AutoPropertyResolver : NSObject() {
    
    private val storage = mutableMapOf<String, Any?>()
    
    @ObjCMethod
    override fun resolveInstanceMethod(sel: COpaquePointer?): Boolean {
        val selectorName = sel?.let { sel_getName(it)?.toKString() } ?: ""
        
        when {
            // Getter: propertyName
            selectorName.matches(Regex("^[a-z][a-zA-Z0-9]*$")) -> {
                val getter = staticCFunction { 
                    self: COpaquePointer?, _cmd: COpaquePointer? -> 
                    (interpretObjCPointer<AutoPropertyResolver>(self) as? AutoPropertyResolver)
                        ?.storage?.get(selectorName)
                }
                
                class_addMethod(
                    object_getClass(this),
                    sel,
                    getter.reinterpret(),
                    "@@:"
                )
                return true
            }
            
            // Setter: setPropertyName:
            selectorName.startsWith("set") && selectorName.endsWith(":") -> {
                val propertyName = selectorName
                    .removePrefix("set")
                    .removeSuffix(":")
                    .replaceFirstChar { it.lowercase() }
                
                val setter = staticCFunction { 
                    self: COpaquePointer?, _cmd: COpaquePointer?, value: COpaquePointer? ->
                    (interpretObjCPointer<AutoPropertyResolver>(self) as? AutoPropertyResolver)
                        ?.storage?.set(propertyName, value)
                }
                
                class_addMethod(
                    object_getClass(this),
                    sel,
                    setter.reinterpret(),
                    "v@:@"
                )
                return true
            }
        }
        
        return super.resolveInstanceMethod(sel)
    }
}

快速转发

FastForwarder - 快速转发实现

kotlin
// src/iosMain/kotlin/FastForwarder.kt
@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)

import kotlinx.cinterop.*
import platform.Foundation.*
import platform.objc.*

/**
 * 快速转发代理
 */
@ObjCClass
class FastForwarder(
    private val target: NSObject
) : NSObject() {
    
    @ObjCMethod
    override fun forwardingTargetForSelector(aSelector: COpaquePointer?): Any? {
        // 将所有消息转发给目标对象
        if (target.respondsToSelector(aSelector)) {
            println("快速转发: ${sel_getName(aSelector)?.toKString()}")
            return target
        }
        
        return super.forwardingTargetForSelector(aSelector)
    }
    
    @ObjCMethod
    override fun respondsToSelector(aSelector: COpaquePointer?): Boolean {
        return target.respondsToSelector(aSelector) ||
               super.respondsToSelector(aSelector)
    }
    
    @ObjCMethod
    override fun methodSignatureForSelector(aSelector: COpaquePointer?): NSMethodSignature? {
        return target.methodSignatureForSelector(aSelector) ?:
               super.methodSignatureForSelector(aSelector)
    }
}

/**
 * 多目标代理
 */
@ObjCClass
class MultiTargetProxy : NSObject() {
    
    private val targets = mutableListOf<NSObject>()
    
    fun addTarget(target: NSObject) {
        targets.add(target)
    }
    
    @ObjCMethod
    override fun forwardingTargetForSelector(aSelector: COpaquePointer?): Any? {
        // 找第一个能响应的目标
        for (target in targets) {
            if (target.respondsToSelector(aSelector)) {
                return target
            }
        }
        
        return super.forwardingTargetForSelector(aSelector)
    }
    
    @ObjCMethod
    override fun respondsToSelector(aSelector: COpaquePointer?): Boolean {
        return targets.any { it.respondsToSelector(aSelector) } ||
               super.respondsToSelector(aSelector)
    }
}

完整消息转发

FullForwarder - 完整转发实现

kotlin
// src/iosMain/kotlin/FullForwarder.kt
@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)

import kotlinx.cinterop.*
import platform.Foundation.*
import platform.objc.*

/**
 * 完整消息转发基类
 */
@ObjCClass
abstract class FullForwarder : NSObject() {
    
    @ObjCMethod
    override fun methodSignatureForSelector(aSelector: COpaquePointer?): NSMethodSignature? {
        // 子类实现
        return getMethodSignature(aSelector)
    }
    
    @ObjCMethod
    override fun forwardInvocation(anInvocation: NSInvocation) {
        // 子类实现
        handleInvocation(anInvocation)
    }
    
    abstract fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature?
    abstract fun handleInvocation(anInvocation: NSInvocation)
}

/**
 * 日志代理
 */
@ObjCClass
class LoggingProxy(
    private val target: NSObject
) : FullForwarder() {
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return target.methodSignatureForSelector(aSelector)
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        val selectorName = sel_getName(anInvocation.selector)?.toKString() ?: ""
        
        println("━━━ 方法调用日志 ━━━")
        println("方法: $selectorName")
        println("参数数量: ${anInvocation.methodSignature.numberOfArguments}")
        
        val startTime = NSDate().timeIntervalSince1970
        
        // 执行实际方法
        anInvocation.invokeWithTarget(target)
        
        val endTime = NSDate().timeIntervalSince1970
        val duration = (endTime - startTime) * 1000
        
        println("耗时: ${String.format("%.2f", duration)}ms")
        println("━━━━━━━━━━━━━━━━━━━")
    }
    
    @ObjCMethod
    override fun respondsToSelector(aSelector: COpaquePointer?): Boolean {
        return target.respondsToSelector(aSelector)
    }
}

/**
 * 性能分析代理
 */
@ObjCClass
class PerformanceProxy(
    private val target: NSObject
) : FullForwarder() {
    
    data class PerformanceMetrics(
        var callCount: Int = 0,
        var totalTime: Double = 0.0,
        var minTime: Double = Double.MAX_VALUE,
        var maxTime: Double = 0.0
    )
    
    private val metrics = mutableMapOf<String, PerformanceMetrics>()
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return target.methodSignatureForSelector(aSelector)
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        val selectorName = sel_getName(anInvocation.selector)?.toKString() ?: ""
        
        val startTime = NSDate().timeIntervalSince1970
        anInvocation.invokeWithTarget(target)
        val endTime = NSDate().timeIntervalSince1970
        
        val duration = (endTime - startTime) * 1000
        
        val metric = metrics.getOrPut(selectorName) { PerformanceMetrics() }
        metric.callCount++
        metric.totalTime += duration
        metric.minTime = minOf(metric.minTime, duration)
        metric.maxTime = maxOf(metric.maxTime, duration)
    }
    
    fun printReport() {
        println("\n━━━ 性能分析报告 ━━━")
        metrics.forEach { (method, metric) ->
            val avgTime = metric.totalTime / metric.callCount
            println("""
                方法: $method
                调用次数: ${metric.callCount}
                总耗时: ${String.format("%.2f", metric.totalTime)}ms
                平均耗时: ${String.format("%.2f", avgTime)}ms
                最小耗时: ${String.format("%.2f", metric.minTime)}ms
                最大耗时: ${String.format("%.2f", metric.maxTime)}ms
            """.trimIndent())
        }
        println("━━━━━━━━━━━━━━━━━━━\n")
    }
}

代理模式

ProxyPatterns - 常见代理模式

kotlin
// src/iosMain/kotlin/ProxyPatterns.kt
@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)

import kotlinx.cinterop.*
import platform.Foundation.*
import platform.objc.*

/**
 * 惰性加载代理
 */
@ObjCClass
class LazyProxy : FullForwarder() {
    
    private var realObject: NSObject? = null
    private val objectLoader: () -> NSObject
    
    constructor(loader: () -> NSObject): super() {
        this.objectLoader = loader
    }
    
    private fun loadIfNeeded(): NSObject {
        if (realObject == null) {
            println("📦 延迟加载真实对象...")
            realObject = objectLoader()
        }
        return realObject!!
    }
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return loadIfNeeded().methodSignatureForSelector(aSelector)
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        anInvocation.invokeWithTarget(loadIfNeeded())
    }
    
    @ObjCMethod
    override fun respondsToSelector(aSelector: COpaquePointer?): Boolean {
        return loadIfNeeded().respondsToSelector(aSelector)
    }
}

/**
 * 缓存代理
 */
@ObjCClass
class CachingProxy(
    private val target: NSObject
) : FullForwarder() {
    
    private val cache = mutableMapOf<String, Any?>()
    private val cacheableSelectors = setOf("description", "count", "length")
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return target.methodSignatureForSelector(aSelector)
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        val selectorName = sel_getName(anInvocation.selector)?.toKString() ?: ""
        
        if (selectorName in cacheableSelectors) {
            val cached = cache[selectorName]
            if (cached != null) {
                println("🎯 缓存命中: $selectorName")
                anInvocation.setReturnValue(cached)
                return
            }
        }
        
        anInvocation.invokeWithTarget(target)
        
        if (selector in cacheableSelectors) {
            memScoped {
                val buffer = allocArray<ByteVar>(1024)
                anInvocation.getReturnValue(buffer)
                cache[selectorName] = buffer
            }
        }
    }
}

/**
 * 权限检查代理
 */
@ObjCClass
class PermissionProxy(
    private val target: NSObject,
    private val permissionChecker: (String) -> Boolean
) : FullForwarder() {
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return target.methodSignatureForSelector(aSelector)
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        val selectorName = sel_getName(anInvocation.selector)?.toKString() ?: ""
        
        if (permissionChecker(selectorName)) {
            anInvocation.invokeWithTarget(target)
        } else {
            println("🚫 权限拒绝: $selectorName")
            throw NSException(
                name = "PermissionDenied",
                reason = "No permission to call $selectorName",
                userInfo = null
            )
        }
    }
}

AOP 框架

AOPFramework - 切面编程

kotlin
// src/iosMain/kotlin/AOPFramework.kt
@file:OptIn(ExperimentalForeignApi::class, BetaInteropApi::class)

import kotlinx.cinterop.*
import platform.Foundation.*
import platform.objc.*

/**
 * AOP拦截器接口
 */
interface Interceptor {
    fun before(invocation: NSInvocation)
    fun after(invocation: NSInvocation, result: Any?)
    fun onError(invocation: NSInvocation, error: Exception)
}

/**
 * AOP代理
 */
@ObjCClass
class AOPProxy(
    private val target: NSObject
) : FullForwarder() {
    
    private val interceptors = mutableListOf<Interceptor>()
    
    fun addInterceptor(interceptor: Interceptor) {
        interceptors.add(interceptor)
    }
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return target.methodSignatureForSelector(aSelector)
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        // Before advice
        interceptors.forEach { it.before(anInvocation) }
        
        var error: Exception? = null
        var result: Any? = null
        
        try {
            anInvocation.invokeWithTarget(target)
            
            // 获取返回值
            memScoped {
                val buffer = allocArray<ByteVar>(1024)
                anInvocation.getReturnValue(buffer)
                result = buffer
            }
        } catch (e: Exception) {
            error = e
        }
        
        // After advice
        if (error != null) {
            interceptors.forEach { it.onError(anInvocation, error) }
        } else {
            interceptors.forEach { it.after(anInvocation, result) }
        }
    }
}

/**
 * 日志拦截器
 */
class LoggingInterceptor : Interceptor {
    override fun before(invocation: NSInvocation) {
        val methodName = sel_getName(invocation.selector)?.toKString()
        println("→ 调用: $methodName")
    }
    
    override fun after(invocation: NSInvocation, result: Any?) {
        val methodName = sel_getName(invocation.selector)?.toKString()
        println("← 返回: $methodName")
    }
    
    override fun onError(invocation: NSInvocation, error: Exception) {
        val methodName = sel_getName(invocation.selector)?.toKString()
        println("✗ 错误: $methodName - ${error.message}")
    }
}

/**
 * 性能监控拦截器
 */
class PerformanceInterceptor : Interceptor {
    private var startTime = 0.0
    
    override fun before(invocation: NSInvocation) {
        startTime = NSDate().timeIntervalSince1970
    }
    
    override fun after(invocation: NSInvocation, result: Any?) {
        val duration = (NSDate().timeIntervalSince1970 - startTime) * 1000
        val methodName = sel_getName(invocation.selector)?.toKString()
        
        if (duration > 100) {
            println("⚠️  慢方法: $methodName (${String.format("%.2f", duration)}ms)")
        }
    }
    
    override fun onError(invocation: NSInvocation, error: Exception) {
        // 不处理
    }
}

性能对比

转发类型耗时(相对直接调用)适用场景
直接调用1x (基准)正常方法调用
动态方法解析1.2x一次性解析,后续缓存
快速转发1.5x简单代理,无需修改调用
完整转发3.8xAOP, 日志, 性能分析

实战案例

案例一:远程对象代理

kotlin
@ObjCClass
class RemoteObjectProxy(
    private val serverURL: String
) : FullForwarder() {
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        // 返回默认签名
        return NSMethodSignature.signatureWithObjCTypes("@@:@")
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        val methodName = sel_getName(anInvocation.selector)?.toKString() ?: ""
        
        // 序列化参数
        val params = serializeParams(anInvocation)
        
        // 发送RPC请求
        val response = sendRPCRequest(serverURL, methodName, params)
        
        // 反序列化结果
        val result = deserializeResult(response)
        anInvocation.setReturnValue(result)
    }
    
    private fun serializeParams(invocation: NSInvocation): String {
        // 实现参数序列化
        return "{}"
    }
    
    private fun sendRPCRequest(url: String, method: String, params: String): String {
        // 实现HTTP请求
        return "{}"
    }
    
    private fun deserializeResult(response: String): Any? {
        // 实现结果反序列化
        return null
    }
}

案例二:Mock对象生成器

kotlin
@ObjCClass
class MockObject : FullForwarder() {
    
    private val returnValues = mutableMapOf<String, Any?>()
    
    fun stub(methodName: String, returnValue: Any?) {
        returnValues[methodName] = returnValue
    }
    
    override fun getMethodSignature(aSelector: COpaquePointer?): NSMethodSignature? {
        return NSMethodSignature.signatureWithObjCTypes("@@:")
    }
    
    override fun handleInvocation(anInvocation: NSInvocation) {
        val methodName = sel_getName(anInvocation.selector)?.toKString() ?: ""
        val returnValue = returnValues[methodName]
        
        if (returnValue != null) {
            anInvocation.setReturnValue(returnValue)
        }
    }
}

// 使用
fun testExample() {
    val mock = MockObject()
    mock.stub("description", "Mocked description")
    mock.stub("count", 42)
    
    // 测试代码使用mock对象
}

消息转发机制是 ObjC 运行时最强大的特性,为 Kotlin/Native 提供了实现代理模式、AOP、惰性加载、Mock对象和远程调用的能力,是构建灵活、可扩展 iOS 应用的关键技术。