在KMM项目中能否通过反射获取类的声明函数?
在KMM中实现获取带指定注解的类方法
KMM的CommonMain无法直接使用Kotlin标准库的反射API(比如declaredFunctions、hasAnnotation),因为iOS平台对Kotlin反射的支持有限,必须通过expect/actual机制分平台实现。以下是完整方案,重点说明iOS端的实现:
1. 定义Common层的预期接口
在CommonMain中声明通用的反射功能接口,由各平台实现:
// CommonMain/AnnotationReflector.kt expect class AnnotationReflector { fun <T : Annotation> getMethodsWithAnnotation(clazz: KClass<*>, annotationClass: KClass<T>): List<KFunction<*>> }
2. Android端实现(参考)
Android端直接复用Kotlin/JVM反射API,和你已有的实现逻辑一致:
// AndroidMain/AnnotationReflector.kt actual class AnnotationReflector { actual fun <T : Annotation> getMethodsWithAnnotation(clazz: KClass<*>, annotationClass: KClass<T>): List<KFunction<*>> { return clazz.declaredFunctions.filter { it.hasAnnotation<T>() } } }
3. iOS端实现
iOS端需要借助Objective-C Runtime来实现,因为Kotlin/Native的反射不支持直接获取注解和方法。
3.1 确保注解在iOS端可见
你的MyAnnotation注解必须添加@ObjCAnnotation,才能被ObjC Runtime识别,同时保留运行时注解:
// CommonMain/MyAnnotation.kt @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.FUNCTION) @ObjCAnnotation // 关键:让注解能被ObjC Runtime识别 annotation class MyAnnotation
3.2 iOS端的具体实现代码
在iOSMain中编写如下代码,通过ObjC Runtime遍历方法并检查注解,再映射回Kotlin的KFunction:
// iOSMain/AnnotationReflector.kt import kotlinx.cinterop.* import objc.runtime.* actual class AnnotationReflector { actual fun <T : Annotation> getMethodsWithAnnotation(clazz: KClass<*>, annotationClass: KClass<T>): List<KFunction<*>> { val matchedMethods = mutableListOf<KFunction<*>>() // 获取Kotlin类对应的Objective-C类 val objcClass = clazz.objCClass ?: return matchedMethods // 遍历类的所有方法 var methodCount = UIntVar() val methods = class_copyMethodList(objcClass, methodCount) ?: return matchedMethods try { for (index in 0 until methodCount.value.toInt()) { val method = methods[index] ?: continue val selector = method_getName(method) val objcMethodName = NSStringFromSelector(selector) // 检查当前方法是否带有目标注解 if (methodHasTargetAnnotation(method, annotationClass.javaClass.name)) { // 通过方法名匹配Kotlin侧的KFunction clazz.declaredFunctions.firstOrNull { it.name == objcMethodName }?.let { matchedMethods.add(it) } } } } finally { methods?.let { free(it) } } return matchedMethods } // 辅助方法:检查方法是否包含指定注解 private fun methodHasTargetAnnotation(method: COpaquePointer, annotationClassName: String): Boolean { val annotationAttr = method_copyAttributeValue(method, "annotations") ?: return false try { val annotationStr = annotationAttr.toKString() // 注解字符串格式类似 "@com.example.MyAnnotation()",直接判断类名是否存在 return annotationClassName in annotationStr } finally { free(annotationAttr) } } }
iOS端注意事项
- 方法名匹配需要注意Kotlin到ObjC的转换规则:比如Kotlin的
fun test(param: String)在ObjC中会变成testWithParam:,如果你的方法带参数,需要调整objcMethodName和Kotlin方法名的匹配逻辑,或者在注解中额外标记方法名。 - 如果注解带有参数,
methodHasTargetAnnotation中的判断逻辑需要更严谨,比如解析完整的注解字符串结构。
通用调用方式
在CommonMain中直接使用即可:
fun testMethod() { val reflector = AnnotationReflector() val targetMethods = reflector.getMethodsWithAnnotation(MyClass::class, MyAnnotation::class) targetMethods.forEach { // 执行你的逻辑 } }
内容的提问来源于stack exchange,提问作者Zhou Haibo
相关产品推荐
相关产品推荐

