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

时间:2016-12-02 22:38:42

标签: xamarin crash xamarin.ios runtimeexception

调用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

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

我没有改变似乎有所作为。有没有人遇到这个或有任何调试技巧?

1 个答案:

答案 0 :(得分:1)

问题

Xamarin的PerformSelector重载需要两个参数,期望选择器返回一个值。 即。

Xamarin.iOS.Foundation.NSObject

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

然后将其发送到尝试编组返回的本机方法。

在问题代码中,表的委托的tableView:didSelectRowAtIndexPath方法返回void。因此,本机代码试图编组nil,这导致了错误。

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

解决方案

首先,定义原生方法......

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));