消息转发机制
本文深入讲解 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.ktsGradle 配置
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.8x | AOP, 日志, 性能分析 |
实战案例
案例一:远程对象代理
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 应用的关键技术。