首页 > 解决方案 > 本机 iOS 类中的 j2objc 反射支持

问题描述

我刚刚将我的 j2objc 工具从 1.0.2 更新到 2.8。据我所知,从 1.1 版本开始,反射发生了变化。现在代码不再工作了。

我们有IApplicationSupport.java接口。像这样的东西:

public interface IApplicationSupport
{
    void openSupport();
}

此代码使用 j2objc 成功转换为 Objective-С

然后我们有平台相关的类AndroidApplicationSupport并且IosApplicationSupport没有被翻译。Android部分还可以,但iOS有一些问题。

稍后在代码中的某个地方我需要调用[IOSClass classForIosName:@"IosApplicationSupport"]. 这就是问题所在。每次我都会得到下一个异常com.google.j2objc.ReflectionStrippedError: IosApplicationSupport: Reflection is unavailable. Fix this by avoiding reflection or building without --strip-reflection.当然我--strip-reflection在翻译过程中不使用标志(但我相信问题不存在)。在我的情况下,避免反射似乎非常困难,因为使用了太多带有反射的类。

我知道,翻译的课程有相当大的__metadata方法。有没有办法模拟本机类的元数据以启用反射支持?如果我必须添加元数据,是否有任何关于如何生成的规则?我们的项目很重,就靠它了。

谢谢你的帮助!

标签: androidiosreflectionj2objc

解决方案


当然,__metadata method可以很容易地添加。最简单的方法是创建一个简单的存根文件,它定义了包、类和方法,然后将其翻译一次并将生成的方法复制到您的本机类中。这是 IosApplicationSupport.java 的示例存根(我不得不猜测 IApplicationSupport 在哪个包中):

import foo.bar.IApplicationSupport;

public class IosApplicationSupport implements IApplicationSupport {
  public void openSupport() {
  }
}

在这个类上运行 j2objc 后,它的元数据是:

+ (const J2ObjcClassInfo *)__metadata {
  static J2ObjcMethodInfo methods[] = {
    { NULL, NULL, 0x1, -1, -1, -1, -1, -1, -1 },
    { NULL, "V", 0x1, -1, -1, -1, -1, -1, -1 },
  };
  #pragma clang diagnostic push
  #pragma clang diagnostic ignored "-Wobjc-multiple-method-names"
  #pragma clang diagnostic ignored "-Wundeclared-selector"
  methods[0].selector = @selector(init);
  methods[1].selector = @selector(openSupport);
  #pragma clang diagnostic pop
  static const J2ObjcClassInfo _IosApplicationSupport = { "IosApplicationSupport", NULL, NULL, methods, NULL, 7, 0x1, 2, 0, -1, -1, -1, -1, -1 };
  return &_IosApplicationSupport;
}

(这看起来很大,但在 64 位处理器上只有 128 个字节(NSLog(@"sizeof metadata: %ld", sizeof(_IosApplicationSupport) + sizeof(methods));)。)

但是,如果您只是加载一个类并创建它的默认实例,则不需要元数据,因为 Objective-C 运行时直接支持这一点。由于您没有说包在什么IApplicationSupport中,因此使用foo.bar包成功执行此代码而无需元数据:

  IOSClass *appSupportClass = [IOSClass classForIosName:@"IosApplicationSupport"];
  id<FooBarIApplicationSupport> appSupport = [appSupportClass newInstance];
  [appSupport openSupport];

由于类加载和实例创建工作,听起来 IosApplicationSupport 使用 java.lang.reflect API,它需要元数据。但是,从 1.0.2 开始,有一种比手动编码 IosApplicationSupport 更简单的方法:使用OCNI。OCNI 允许您将 Objective-C 直接嵌入到 Java 本地方法中。我们在 JRE 仿真库中经常使用它,例如这里,本地 iOS MD5 签名是在 Java 类中完成的。这避免了必须定义和维护元数据结构,只需在 Java 中编写代码并在必要的地方插入 Objective-C。

所以我的建议是用Java重写IosApplicationSupport,在需要本地代码的地方用OCNI定义本地Java方法。


推荐阅读