如果我使用任何非null 参数调用以下RoboVM方法:
public static void runOnUiThread(final Runnable runnable) {
System.out.println("Inside runOnUiThread():");
System.out.println(" Null-check: "+(runnable==null));
NSOperation operation = new NSOperation() {
@Override
public void main() {
System.out.println("Inside main():");
System.out.println(" Null-check: "+(runnable==null));
runnable.run(); // NullPointerException here?!? How???
System.out.println(" main() completed");
}
};
NSOperationQueue.getMainQueue().addOperation(operation);
}
输出:
Inside runOnUiThread():
Null-check: false
Inside main():
Null-check: true
java.lang.NullPointerException
at RoboVMTools$1.main(RoboVMTools.java)
at org.robovm.apple.foundation.NSOperation.$cb$main(NSOperation.java)
at org.robovm.apple.uikit.UIApplication.main(Native Method)
at org.robovm.apple.uikit.UIApplication.main(UIApplication.java)
at Main.main(Main.java)
到底是怎么回事?更重要的是,我该如何解决它?
operation.addStrongRef(runnable);
之前添加NSOperationQueue...
。没有区别。private final
字段来存储传递给其构造函数的runnable。结果相同。我只是错过了一些非常明显的东西吗?
答案 0 :(得分:1)
你对GC是正确的。在从Objective-C端调用操作之前,您的NSOperation
实例是垃圾收集的。当NSOperationQueue
调用Java端时,将创建一个NSOperation
匿名类的新实例,该实例没有对Runnable
实例的引用,而是null
结果是NullPointerException
被抛出。
使用addStrongRef()
解决问题的方法是正确的,但只有mainQueue.addStrongRef(operation)
和相应的removeStrongRef()
调用才足够:
public static void runOnUiThread(final Runnable runnable) {
final NSOperationQueue mainQueue = NSOperationQueue.getMainQueue();
NSOperation operation = new NSOperation() {
@Override
public void main() {
runnable.run();
mainQueue.removeStrongRef(this);
}
};
mainQueue.addStrongRef(operation);
mainQueue.addOperation(operation);
}
这将阻止Java operation
实例(以及从Runnable
可以访问的任何Java对象)进行GC操作,直到释放Objective-C NSOperationQueue
实例。由于Objective-C端队列是一个单例,它在应用程序的生命周期内不会被释放。
RoboVM NSOperationQueue
Java类提供addOperation()
方法的版本,该方法采用Runnable
。使用此方法时,RoboVM将负责保留Objective {C端所需的Runnable
实例。对于采用@Block
带注释的Runnable
类型参数或任何org.robovm.objc.block.VoidBlock*
或org.robovm.objc.block.Block*
接口的方法,情况也是如此。
使用此addOperation()
方法,您的代码将变为:
public static void runOnUiThread(Runnable runnable) {
NSOperationQueue.getMainQueue().addOperation(runnable);
}
PS。 RoboVM使用的GC与Apple垃圾收集器无关,因此Apple的文档不会帮助您理解这样的问题。
答案 1 :(得分:0)
嗯......这解决了它:
public static void runOnUiThread(final Runnable runnable) {
final NSOperationQueue mainQueue = NSOperationQueue.getMainQueue();
NSOperation operation = new NSOperation() {
@Override
public void main() {
runnable.run();
mainQueue.removeStrongRef(runnable);
mainQueue.removeStrongRef(this );
}
};
mainQueue.addStrongRef(runnable );
mainQueue.addStrongRef(operation);
mainQueue.addOperation(operation);
}
但不要问我为什么这是必要的。 Apple文档说"In garbage-collected applications, the queue strongly references the operation object."所以,我之前尝试的operation.addStrongRef(runnable);
应该已经足够了,因为操作对象应该被队列引用。但我猜这个世界并不总是像我解释文档那样工作。