如何使用 MonoTouch.ObjCRuntime.Selector 和 Execute Selector 发送参数

本文关键字:Selector 参数 Execute MonoTouch ObjCRuntime 何使用 | 更新日期: 2023-09-27 18:33:18

这是我找到的一个例子,但他们省略了实际发送参数。

this.PerformSelector(new MonoTouch.ObjCRuntime.Selector("_HandleSaveButtonTouchUpInside"),null,0.0f);
[Export("_HandleSaveButtonTouchUpInside")]
    void _HandleSaveButtonTouchUpInside()
    {
...
}

我希望能够做这样的事情:

this.PerformSelector(new MonoTouch.ObjCRuntime.Selector("_HandleSaveButtonTouchUpInside"),null,0.0f);
[Export("_HandleSaveButtonTouchUpInside")]
    void _HandleSaveButtonTouchUpInside(NSURL url, NSData data)
    {
...
}

如何更改执行选择器调用以将参数发送到该方法?

如何使用 MonoTouch.ObjCRuntime.Selector 和 Execute Selector 发送参数

MonoTouch 文档指出该方法映射到 Obj-C 选择器 performSelector:withObject:afterDelay ,它仅支持使用单个参数调用选择器。

处理此问题的最佳方法取决于您需要做什么。处理此问题的一种典型方法是将参数作为属性/字段放在单个 NSObject 上,然后将目标修改为具有单个参数,并从该方法中提取真正的参数。如果使用自定义 MonoTouch 对象执行此操作,则必须注意收集托管对等方的 GC,如果托管代码中没有任何内容保留对它的引用。

更好的解决方案将取决于您如何使用它。例如,在您的示例中,您可以简单地直接调用 C# 方法,例如

_HandleSaveButtonTouchUpInside (url, data);

如果您出于某种原因需要通过 Obj-C 进行调度,但不需要延迟,请使用 MonoTouch.ObjCRuntime.Messaging,例如

MonoTouch.ObjCRuntime.Messaging.void_objc_msgSend_IntPtr_IntPtr (
    target.Handle,
    MonoTouch.ObjCRuntime.Selector.GetHandle ("_HandleSaveButtonTouchUpInside"),
    arg0.Handle,
    arg1.Handle);

如果需要延迟,可以使用 NSTimer。MonoTouch 为此添加了特殊支持以使用 NSAction 委托,因此您可以使用 C# lambda 安全地捕获参数。

NSTimer.CreateScheduledTimer (someTimespan, () => _HandleSaveButtonTouchUpInside (url, data));

我也找不到此调用的绑定。 在下面的示例中,我为 PerformSelector 添加了自己的重载。 也许其中一位 Xamarin 工程师可以证实这一点。

using System;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using MonoTouch.ObjCRuntime;
using System.Runtime.InteropServices;
namespace delete20120506
{
    [Register ("AppDelegate")]
    public partial class AppDelegate : UIApplicationDelegate
    {
        UIWindow window;
        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            window = new UIWindow (UIScreen.MainScreen.Bounds);
            // 
            Target target = new Target ();
            NSUrl url = new NSUrl ("http://xamarin.com/");
            NSData nsData = NSData.FromString ("Hello");
            target.PerformSelector (new MonoTouch.ObjCRuntime.Selector 
                                  ("TestSelUrl:withData:"), url, nsData);
            window.MakeKeyAndVisible ();
            return true;
        }
    }
    [Register ("Target")]
    public class Target : NSObject
    {
        public Target () : base (NSObjectFlag.Empty) {}
        [Export("TestSelUrl:withData:")]
        void TestSelUrlWithData(NSUrl url, NSData nsData)
        {
            Console.WriteLine ("In TestSelUrlWithData");
            Console.WriteLine (url.ToString ());
            Console.WriteLine (nsData.ToString ());
            return;
        }
        [DllImport ("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSend")]
        public static extern void void_objc_msgSend_intptr_intptr_intptr (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, IntPtr arg3);
        [DllImport ("/usr/lib/libobjc.dylib", EntryPoint = "objc_msgSendSuper")]
        public static extern void void_objc_msgSendSuper_intptr_intptr_intptr (IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, IntPtr arg3);

        public virtual void PerformSelector (MonoTouch.ObjCRuntime.Selector sel, 
                                              NSObject arg1, NSObject arg2)
        {
            if (this.IsDirectBinding)
            {
                void_objc_msgSend_intptr_intptr_intptr (this.Handle, 
                    Selector.GetHandle ("performSelector:withObject:withObject:"),
                    sel.Handle, arg1.Handle, arg2.Handle);
            }
            else
            {
                void_objc_msgSendSuper_intptr_intptr_intptr (this.SuperHandle,
                    Selector.GetHandle ("performSelector:withObject:withObject:"), sel.Handle, 
                    arg1.Handle, arg2.Handle);
            }
        }
    }
}