Unrecognized selector sent to instance

I want implement mechanism to run methods in main thread. I have code:

@Override
public void performOnMainThread(Runnable r) {
    MainThreadTransporter transporter = new MainThreadTransporter(r);
    SEL selector = new SEL("runOnMainThread:");
    mainThreadHandler.performSelectorOnMainThreadWithObjectWaitUntilDone(selector, transporter, false);
}

and class:

@org.moe.natj.general.ann.Runtime(ObjCRuntime.class)
@ObjCClassName("MainThreadHandler")
@RegisterOnStartup
static class MainThreadHandler extends NSObject {
    private MainThreadHandler(Pointer peer)
    {
        super(peer);
    }

    @Owned
    @Selector("alloc")
    public static native MainThreadHandler alloc();

    @Selector("init")
    public native MainThreadHandler init();

    @Selector("runOnMainThread:")
    private static void runOnMainThread(MainThreadHandler self, Selector cmd, IOSNativeUtilityFactory.MainThreadTransporter t) {
        t.getRunnable().run();
    }
}

With robovm that way works, but with MOE I see exception:

2017-01-10 13:10:54.198 myproject[2635:84834] -[MainThreadHandler runOnMainThread:]: unrecognized selector sent to instance 0x7fbf314adfc0
org.moe.natj.objc.ObjCException: -[MainThreadHandler runOnMainThread:]: unrecognized selector sent to instance 0x7fbf314adfc0
at apple.uikit.c.UIKit.UIApplicationMain(Native Method)
at com.company.AppClient.main(AppClient.java:20)

Dear Ivan Bielko,

I would recommend using something like this:

public void runOnMain(Runnable r) {
    Globals.dispatch_async(Globals.dispatch_get_main_queue(), r::run);
}

Test code:

@Override
public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) {
    Globals.dispatch_async(Globals.dispatch_get_global_queue(0, 0), () -> {
        runOnMain(() -> {
            System.out.println();
        });
    });
    return true;
}

But if you want to use performSelectorOnMainThreadWithObjectWaitUntilDone, then:

import apple.NSObject;
import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.Owned;
import org.moe.natj.objc.SEL;
import org.moe.natj.objc.ann.Selector;

public class MainThreadPerformer extends NSObject {

    @Owned
    @Selector("alloc")
    public static native MainThreadPerformer alloc();

    @Selector("init")
    public native MainThreadPerformer init();

    private Runnable runnable;

    protected MainThreadPerformer(Pointer peer) {
        super(peer);
    }

    @Selector("run")
    public void run() {
        if (runnable != null) {
            runnable.run();
        }
    }

    public void dispatchAsync() {
        performSelectorOnMainThreadWithObjectWaitUntilDone(new SEL("run"), null, false);
    }

    public static void dispatchAsyncOnMain(Runnable pRunnable) {
        if (pRunnable == null) {
            throw new NullPointerException();
        }
        final MainThreadPerformer performer = MainThreadPerformer.alloc().init();
        performer.runnable = pRunnable;
        performer.dispatchAsync();
    }
}

Test code:

    @Override
    public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) {
        Globals.dispatch_async(Globals.dispatch_get_global_queue(0, 0), () -> {
            MainThreadPerformer.dispatchAsyncOnMain(() -> {
                System.out.println();
            });
        });
        return true;
    }

Best Regards,
Kristóf

1 Like

Thank you. Everything works fine.