Xamarin.iOS 调用 PerformanceSelector() 时出现运行时错误导致应用程序崩溃

Xamarin.iOS Runtime Error on call to PerformanceSelector() makes app crash

调用 PerformSelector 时...

    var tableView = view.GetAncestors().OfType2<UITableView>().First2();
    var indexPath = tableView.IndexPathForCell(view.To<UITableViewCell>());

    tableView.SelectRow(indexPath, true, UITableViewScrollPosition.None);

    tableView
        .PerformSelector(new Selector("delegate"))
        .If(_ => _.RespondsToSelector(new Selector("tableView:didSelectRowAtIndexPath:")))
        .SafeNav(_ => _.PerformSelector(new Selector("tableView:didSelectRowAtIndexPath:"), tableView, indexPath));

我在 ObjCRuntime 中遇到了这个未知异常...

Warning (13816) / MonoTouchClient: critical: Stacktrace:
Warning (13816) / MonoTouchClient: critical:   at <unknown> <0xffffffff>
Warning (13816) / MonoTouchClient: critical:   at (wrapper managed-to-native) ObjCRuntime.Messaging.IntPtr_objc_msgSend (intptr,intptr) <0x00057>
Warning (13816) / MonoTouchClient: critical:   at ObjCRuntime.Class.GetClassForObject (intptr) [0x00000] in /Users/builder/data/lanes/3969/44931ae8/source/xamarin-macios/src/ObjCRuntime/Class.cs:134
Warning (13816) / MonoTouchClient: critical:   at ObjCRuntime.Runtime.GetNSObject (intptr,ObjCRuntime.Runtime/MissingCtorResolution,bool) [0x00022] in /Users/builder/data/lanes/3969/44931ae8/source/xamarin-macios/src/ObjCRuntime/Runtime.cs:1017
Warning (13816) / MonoTouchClient: critical:   at ObjCRuntime.Runtime.GetNSObject (intptr) [0x00000] in /Users/builder/data/lanes/3969/44931ae8/source/xamarin-macios/src/ObjCRuntime/Runtime.cs:1005
Warning (13816) / MonoTouchClient: critical:   at Foundation.NSObject.PerformSelector (ObjCRuntime.Selector,Foundation.NSObject,Foundation.NSObject) [0x0006f] in /Users/builder/data/lanes/3969/44931ae8/source/xamarin-macios/src/build/ios/native/Foundation/NSObject.g.cs:439
Warning (13816) / MonoTouchClient: critical:
Warning (13816) / MonoTouchClient: critical:
Warning (13816) / MonoTouchClient: critical:    0   MonoTouchClient                     0x00083f11 mono_handle_native_sigsegv + 240
Warning (13816) / MonoTouchClient: critical:    1   MonoTouchClient                     0x0008ba15 mono_sigsegv_signal_handler + 222
Warning (13816) / MonoTouchClient: critical:    2   libsystem_platform.dylib            0x2203a077 _sigtramp + 42
Warning (13816) / MonoTouchClient: critical:    3   MonoTouchClient                     0x007c19d8 wrapper_managed_to_native_ObjCRuntime_Messaging_IntPtr_objc_msgSend_intptr_intptr + 100
Warning (13816) / MonoTouchClient: critical:    4   MonoTouchClient                     0x0074c9e0 ObjCRuntime_Class_GetClassForObject_intptr + 44
Warning (13816) / MonoTouchClient: critical:    5   MonoTouchClient                     0x0074a89c ObjCRuntime_Runtime_GetNSObject_intptr_ObjCRuntime_Runtime_MissingCtorResolution_bool + 116
Warning (13816) / MonoTouchClient: critical:    6   MonoTouchClient                     0x0074a81c ObjCRuntime_Runtime_GetNSObject_intptr + 36
Warning (13816) / MonoTouchClient: critical:    7   MonoTouchClient                     0x00770bd0 Foundation_NSObject_PerformSelector_ObjCRuntime_Selector_Foundation_NSObject_Foundation_NSObject + 440

值得注意的是,这只能在 iPad 2 w/ iOS 9.3.5 上重现,而不是 iPad Pro w/ iOS 10.1.1

我所做的任何更改似乎都没有什么不同。有没有人遇到过这个或有任何调试技巧?

问题

Xamarin 的 PerformSelector 重载采用两个参数,需要来自选择器的 return 值。 即

Xamarin.iOS.Foundation.NSObject:

public virtual NSObject PerformSelector(Selector aSelector, NSObject object1, NSObject object2);

然后将其发送到尝试编组 return.

的本机方法

在问题代码中,table 委托的 tableView:didSelectRowAtIndexPath 方法是 returning void。因此,本机代码尝试编组 nil,这导致了错误。

解决方案是为 PerformSelector(Selector aSelector, NSObject object1, NSObject object2) 创建我自己的重载并自己调用本机方法:

解决方案

首先,定义native方法...

public static class MonoTouchNative
{
    [DllImport("/usr/lib/libobjc.dylib")]
    public static extern void objc_msgSend(IntPtr receiver, IntPtr selector, IntPtr arg1, IntPtr arg2, IntPtr arg3);
}

然后,从名为 PerformVoidSelector...

的新扩展方法中调用它
public static void PerformVoidSelector(this NSObject nsObject, Selector aSelector, NSObject object1, NSObject object2)
{
    MonoTouchNative.objc_msgSend(
        nsObject.Handle,
        Selector.GetHandle("performSelector:withObject:withObject:"),
        aSelector.Handle,
        (object1 != null) ? object1.Handle : IntPtr.Zero,
        (object2 != null) ? object2.Handle : IntPtr.Zero);
}

最后,我从应用程序代码中调用了我的新扩展方法...

tableView
    .PerformSelector(new Selector("delegate"))
    .If(_ => _.RespondsToSelector(new                  Selector("tableView:didSelectRowAtIndexPath:")))
    .SafeDo(_ => _.PerformVoidSelector(new Selector("tableView:didSelectRowAtIndexPath:"), tableView, indexPath));