Xamarin.ios의 예외 마샬링Exception Marshaling in Xamarin.iOS

Xamarin.ios에는 예외에 응답 하는 데 도움이 되는 새로운 이벤트 (특히 네이티브 코드)가 포함 되어 있습니다.Xamarin.iOS contains new events to help respond to exceptions, particularly in native code.

관리 코드와 목적-C는 모두 런타임 예외 (try/catch/finally 절)를 지원 합니다.Both managed code and Objective-C have support for runtime exceptions (try/catch/finally clauses).

그러나 해당 구현은 서로 다릅니다. 즉, 런타임 라이브러리 (Mono 런타임 및 목표-C 런타임 라이브러리)가 예외를 처리 하 고 다른 언어로 작성 된 코드를 실행 해야 하는 경우 문제가 발생 합니다.However, their implementations are different, which means that the runtime libraries (the Mono runtime and the Objective-C runtime libraries) have problems when they have to handle exceptions and then run code written in other languages.

이 문서에서는 발생할 수 있는 문제 및 가능한 해결 방법을 설명 합니다.This document explains the problems that can occur, and the possible solutions.

또한 다양 한 시나리오 및 해당 솔루션을 테스트 하는 데 사용할 수 있는 샘플 프로젝트인 예외 마샬링을포함 합니다.It also includes a sample project, Exception Marshaling, which can be used to test different scenarios and their solutions.

문제점Problem

예외가 throw 되 고 스택 해제 중에 throw 된 예외 유형과 일치 하지 않는 프레임이 발견 되 면이 문제가 발생 합니다.The problem occurs when an exception is thrown, and during stack unwinding a frame is encountered which does not match the type of exception that was thrown.

Xamarin.ios 또는 Xamarin.ios에 대 한 일반적인 예는 네이티브 API에서 목표-C 예외를 throw 한 후 스택 해제 프로세스가 관리 되는 프레임에 도달할 때 해당 목표-C 예외를 처리 해야 하는 경우입니다.A typical example of this for Xamarin.iOS or Xamarin.Mac is when a native API throws an Objective-C exception, and then that Objective-C exception must somehow be handled when the stack unwinding process reaches a managed frame.

기본 작업은 아무 작업도 수행 하지 않는 것입니다.The default action is to do nothing. 위의 샘플에서이는 목표-C 런타임이 관리 되는 프레임을 해제 하도록 허용 하는 것을 의미 합니다.For the sample above, this means letting the Objective-C runtime unwind managed frames. 목표-C 런타임이 관리 되는 프레임을 해제 하는 방법을 알지 못하기 때문에 문제가 발생 합니다. 예를 들어 해당 프레임에서 catch 또는 finally 절은 실행 되지 않습니다.This is problematic, because the Objective-C runtime does not know how to unwind managed frames; for example it won't execute any catch or finally clauses in that frame.

손상 된 코드Broken code

다음 코드 예제를 참조 하세요.Consider the following code example:

var dict = new NSMutableDictionary ();
dict.LowLevelSetObject (IntPtr.Zero, IntPtr.Zero); 

이렇게 하면 네이티브 코드에서 목표 C NSInvalidArgumentException이 throw 됩니다.This will throw an Objective-C NSInvalidArgumentException in native code:

NSInvalidArgumentException *** setObjectForKey: key cannot be nil

그리고 스택 추적은 다음과 같습니다.And the stack trace will be something like this:

0   CoreFoundation          __exceptionPreprocess + 194
1   libobjc.A.dylib         objc_exception_throw + 52
2   CoreFoundation          -[__NSDictionaryM setObject:forKey:] + 1015
3   libobjc.A.dylib         objc_msgSend + 102
4   TestApp                 ObjCRuntime.Messaging.void_objc_msgSend_IntPtr_IntPtr (intptr,intptr,intptr,intptr)
5   TestApp                 Foundation.NSMutableDictionary.LowlevelSetObject (intptr,intptr)
6   TestApp                 ExceptionMarshaling.Exceptions.ThrowObjectiveCException ()

프레임 0-3은 네이티브 프레임 이며, 목표-C 런타임의 stack 해제기 이러한 프레임을 해제할 수 있습니다 .Frames 0-3 are native frames, and the stack unwinder in the Objective-C runtime can unwind those frames. 특히 목표-C @catch 또는 @finally 절이 실행 됩니다.In particular, it will execute any Objective-C @catch or @finally clauses.

그러나 목표-C stack 해제기는 프레임을 해제 하지만 관리 되는 예외 논리는 실행 되지 않도록 관리 되는 프레임 (프레임 4-6)을 제대로 해제할 수 없습니다 .However, the Objective-C stack unwinder is not capable of properly unwinding the managed frames (frames 4-6), in that the frames will be unwound, but managed exception logic will not be executed.

즉, 일반적으로 다음과 같은 방법으로 이러한 예외를 catch 할 수 없습니다.Which means that it's usually not possible to catch these exceptions in the following manner:

try {
    var dict = new NSMutableDictionary ();
    dict.LowLevelSetObject (IntPtr.Zero, IntPtr.Zero);
} catch (Exception ex) {
    Console.WriteLine (ex);
} finally {
    Console.WriteLine ("finally");
}

이는 해제기 stack가 관리 되는 catch 절을 알지 못하기 때문 이며 finally 절은 실행 되지 않습니다.This is because the Objective-C stack unwinder does not know about the managed catch clause, and neither will the finally clause be executed.

위의 코드 샘플을 적용 하는 경우 목표-c에는 처리 되지 않은 목표-c 예외, NSSetUncaughtExceptionHandler, xamarin.ios 및 xamarin.ios 사용에 대 한 알림이 포함 되어 있고 해당 지점에서 목표를 변환 하려고 시도 하기 때문입니다. c 관리 되는 예외에 대 한 예외입니다.When the above code sample is effective, it is because Objective-C has a method of being notified of unhandled Objective-C exceptions, NSSetUncaughtExceptionHandler, which Xamarin.iOS and Xamarin.Mac use, and at that point tries to convert any Objective-C exceptions to managed exceptions.

시나리오Scenarios

시나리오 1-관리 되는 catch 처리기를 사용 하 여 목표-C 예외 catchScenario 1 - catching Objective-C exceptions with a managed catch handler

다음 시나리오에서는 관리 되는 catch 처리기를 사용 하 여 목표 C 예외를 catch 할 수 있습니다.In the following scenario, it is possible to catch Objective-C exceptions using managed catch handlers:

  1. 목표-C 예외가 throw 됩니다.An Objective-C exception is thrown.
  2. 목표 C 런타임은 예외를 처리할 수 있는 네이티브 @catch 처리기를 검색 하 여 스택을 탐색 하 고 해제 하지는 않습니다.The Objective-C runtime walks the stack (but does not unwind it), looking for a native @catch handler that can handle the exception.
  3. 목표 C 런타임은 @catch 처리기를 찾지 않고 NSGetUncaughtExceptionHandler를 호출한 다음 Xamarin.ios/Xamarin.ios로 설치 된 처리기를 호출 합니다.The Objective-C runtime doesn't find any @catch handlers, calls NSGetUncaughtExceptionHandler, and invokes the handler installed by Xamarin.iOS/Xamarin.Mac.
  4. Xamarin.ios/Xamarin.ios의 처리기는 목표 C 예외를 관리 되는 예외로 변환 하 고 throw 합니다.Xamarin.iOS/Xamarin.Mac's handler will convert the Objective-C exception into a managed exception, and throw it. 목표 C 런타임에서는 스택을 해제 하지 않았으므로 (한 번만) 현재 프레임은 목표-C 예외가 throw 된 것과 같습니다.Since the Objective-C runtime didn't unwind the stack (only walked it), the current frame is the same one where the Objective-C exception was thrown.

Mono 런타임에서 목표-C 프레임을 제대로 해제 하는 방법을 알지 못하기 때문에 다른 문제가 발생 합니다.Another problem occurs here, because the Mono runtime does not know how to unwind Objective-C frames properly.

Xamarin.ios ' catch 되지 않은 목표-C 예외 콜백이 호출 되 면 스택은 다음과 같습니다.When Xamarin.iOS' uncaught Objective-C exception callback is called, the stack is like this:

 0 libxamarin-debug.dylib   exception_handler(exc=name: "NSInvalidArgumentException" - reason: "*** setObjectForKey: key cannot be nil")
 1 CoreFoundation           __handleUncaughtException + 809
 2 libobjc.A.dylib          _objc_terminate() + 100
 3 libc++abi.dylib          std::__terminate(void (*)()) + 14
 4 libc++abi.dylib          __cxa_throw + 122
 5 libobjc.A.dylib          objc_exception_throw + 337
 6 CoreFoundation           -[__NSDictionaryM setObject:forKey:] + 1015
 7 libxamarin-debug.dylib   xamarin_dyn_objc_msgSend + 102
 8 TestApp                  ObjCRuntime.Messaging.void_objc_msgSend_IntPtr_IntPtr (intptr,intptr,intptr,intptr)
 9 TestApp                  Foundation.NSMutableDictionary.LowlevelSetObject (intptr,intptr) [0x00000]
10 TestApp                  ExceptionMarshaling.Exceptions.ThrowObjectiveCException () [0x00013]

여기에서 관리 되는 프레임은 프레임 8-10 뿐 이지만, 관리 되는 예외는 프레임 0에서 throw 됩니다.Here, the only managed frames are frames 8-10, but the managed exception is thrown in frame 0. 즉, mono 런타임은 네이티브 프레임 0-7을 해제 해야 합니다 .이로 인해 위에서 설명한 문제와 동일한 문제가 발생 합니다. Mono 런타임은 네이티브 프레임을 해제 하지만 목표-C @catch 또는 @finally 절은 실행 하지 않습니다.This means that the Mono runtime must unwind the native frames 0-7, which causes a problem equivalent to the problem discussed above: although the Mono runtime will unwind the native frames, it won't execute any Objective-C @catch or @finally clauses.

코드 예제:Code example:

-(id) setObject: (id) object forKey: (id) key
{
    @try {
        if (key == nil)
            [NSException raise: @"NSInvalidArgumentException"];
    } @finally {
        NSLog (@"This will not be executed");
    }
}

이 프레임을 해제 하는 Mono 런타임이이를 인식 하지 못하기 때문에 @finally 절이 실행 되지 않습니다.And the @finally clause will not be executed because the Mono runtime that unwinds this frame does not know about it.

이는 관리 되는 코드에서 관리 되는 예외를 throw 한 다음 네이티브 프레임을 통해 해제 하 여 첫 번째 관리 되는 catch 절로 가져오는 것입니다.A variation of this is to throw a managed exception in managed code, and then unwinding through native frames to get to the first managed catch clause:

class AppDelegate : UIApplicationDelegate {
    public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
    {
        throw new Exception ("An exception");
    }
    static void Main (string [] args)
    {
        try {
            UIApplication.Main (args, null, typeof (AppDelegate));
        } catch (Exception ex) {
            Console.WriteLine ("Managed exception caught.");
        }
    }
}

관리 되는 UIApplication:Main 메서드는 네이티브 UIApplicationMain 메서드를 호출 하 고, iOS는 궁극적으로 관리 되는 AppDelegate:FinishedLaunching 메서드를 호출 하기 전에 많은 네이티브 코드를 실행 하 고 관리 되는 예외가 throw 되는 경우에도 스택에 네이티브 프레임을 많이 사용 합니다. :The managed UIApplication:Main method will call the native UIApplicationMain method, and then iOS will do a lot of native code execution before eventually calling the managed AppDelegate:FinishedLaunching method, with still a lot of native frames on the stack when the managed exception is thrown:

 0: TestApp                 ExceptionMarshaling.IOS.AppDelegate:FinishedLaunching (UIKit.UIApplication,Foundation.NSDictionary)
 1: TestApp                 (wrapper runtime-invoke) <Module>:runtime_invoke_bool__this___object_object (object,intptr,intptr,intptr) 
 2: libmonosgen-2.0.dylib   mono_jit_runtime_invoke(method=<unavailable>, obj=<unavailable>, params=<unavailable>, exc=<unavailable>, error=<unavailable>)
 3: libmonosgen-2.0.dylib   do_runtime_invoke(method=<unavailable>, obj=<unavailable>, params=<unavailable>, exc=<unavailable>, error=<unavailable>)
 4: libmonosgen-2.0.dylib   mono_runtime_invoke [inlined] mono_runtime_invoke_checked(method=<unavailable>, obj=<unavailable>, params=<unavailable>, error=0xbff45758)
 5: libmonosgen-2.0.dylib   mono_runtime_invoke(method=<unavailable>, obj=<unavailable>, params=<unavailable>, exc=<unavailable>)
 6: libxamarin-debug.dylib  xamarin_invoke_trampoline(type=<unavailable>, self=<unavailable>, sel="application:didFinishLaunchingWithOptions:", iterator=<unavailable>), context=<unavailable>)
 7: libxamarin-debug.dylib  xamarin_arch_trampoline(state=0xbff45ad4)
 8: libxamarin-debug.dylib  xamarin_i386_common_trampoline
 9: UIKit                   -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:]
10: UIKit                   -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:]
11: UIKit                   -[UIApplication _runWithMainScene:transitionContext:completion:]
12: UIKit                   __84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke.3124
13: UIKit                   -[UIApplication workspaceDidEndTransaction:]
14: FrontBoardServices      __37-[FBSWorkspace clientEndTransaction:]_block_invoke_2
15: FrontBoardServices      __40-[FBSWorkspace _performDelegateCallOut:]_block_invoke
16: FrontBoardServices      __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__
17: FrontBoardServices      -[FBSSerialQueue _performNext]
18: FrontBoardServices      -[FBSSerialQueue _performNextFromRunLoopSource]
19: FrontBoardServices      FBSSerialQueueRunLoopSourceHandler
20: CoreFoundation          __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__
21: CoreFoundation          __CFRunLoopDoSources0
22: CoreFoundation          __CFRunLoopRun
23: CoreFoundation          CFRunLoopRunSpecific
24: CoreFoundation          CFRunLoopRunInMode
25: UIKit                   -[UIApplication _run]
26: UIKit                   UIApplicationMain
27: TestApp                 (wrapper managed-to-native) UIKit.UIApplication:UIApplicationMain (int,string[],intptr,intptr)
28: TestApp                 UIKit.UIApplication:Main (string[],intptr,intptr)
29: TestApp                 UIKit.UIApplication:Main (string[],string,string)
30: TestApp                 ExceptionMarshaling.IOS.Application:Main (string[])

0-1 및 27-30 프레임은 관리 되지만 사이에 있는 모든의는 네이티브입니다.Frames 0-1 and 27-30 are managed, while all those in between are native. 이러한 프레임을 통해 Mono가 해제 되 면 목표-C @catch 또는 @finally 절이 실행 되지 않습니다.If Mono unwinds through these frames, no Objective-C @catch or @finally clauses will be executed.

시나리오 2-목표-C 예외를 catch 할 수 없음Scenario 2 - not able to catch Objective-C exceptions

다음 시나리오에서는 목표 C 예외가 다른 방식으로 처리 되었기 때문에 관리 되는 catch 처리기를 사용 하 여 목표-C 예외를 catch 할 수 없습니다 .In the following scenario, it is not possible to catch Objective-C exceptions using managed catch handlers because the Objective-C exception was handled in another way:

  1. 목표-C 예외가 throw 됩니다.An Objective-C exception is thrown.
  2. 목표 C 런타임은 예외를 처리할 수 있는 네이티브 @catch 처리기를 검색 하 여 스택을 탐색 하 고 해제 하지는 않습니다.The Objective-C runtime walks the stack (but does not unwind it), looking for a native @catch handler that can handle the exception.
  3. 목표 C 런타임은 @catch 처리기를 찾고 스택을 해제 하 고 @catch 처리기를 실행 하기 시작 합니다.The Objective-C runtime finds a @catch handler, unwinds the stack, and starts executing the @catch handler.

이 시나리오는 일반적으로 Xamarin.ios 앱에서 찾을 수 있습니다. 주 스레드에는 일반적으로 다음과 같은 코드가 있습니다.This scenario is commonly found in Xamarin.iOS apps, because on the main thread there is usually code like this:

void UIApplicationMain ()
{
    @try {
        while (true) {
            ExecuteRunLoop ();
        }
    } @catch (NSException *ex) {
        NSLog (@"An unhandled exception occured: %@", exc);
        abort ();
    }
}

즉, 주 스레드에서 처리 되지 않은 목표-C 예외가 발생 하지 않으므로 목표-C 예외를 관리 되는 예외로 변환 하는 콜백이 호출 되지 않습니다.This means that on the main thread there's never really an unhandled Objective-C exception, and thus our callback that converts Objective-C exceptions to managed exceptions is never called.

이는 대부분의 UI 개체를 검사 하면 실행 중인 플랫폼에 없는 선택기에 해당 하는 속성을 페치 하기 때문에 Xamarin.ios에서 지 원하는 이전 macOS 버전에서 Xamarin.ios 앱을 디버그 하는 경우에도 매우 일반적입니다. Xamarin.ios에는 더 높은 macOS 버전에 대 한 지원이 포함 되어 있기 때문입니다.This is also quite common when debugging Xamarin.Mac apps on an earlier macOS version than Xamarin.Mac supports because inspecting most UI objects in the debugger will try to fetch properties that correspond to selectors that don't exist on the executing platform (because Xamarin.Mac includes support for a higher macOS version). 이러한 선택기를 호출 하면 NSInvalidArgumentException ("인식할 수 없는 선택기 보냄 ...")이 발생 하 여 프로세스가 충돌 합니다.Calling such selectors will throw an NSInvalidArgumentException ("Unrecognized selector sent to ..."), which eventually causes the process to crash.

요약 하자면, 목표-C 런타임 또는이를 처리 하기 위해 프로그래밍할 수 없는 Mono 런타임 해제 프레임은 크래시, 메모리 누수 및 다른 유형의 예측할 수 없는 (mis) 동작 등의 정의 되지 않은 동작이 발생할 수 있습니다.To summarize, having either the Objective-C runtime or the Mono runtime unwind frames that they are not programmed to handle can lead to undefined behaviors, such as crashes, memory leaks, and other types of unpredictable (mis)behaviors.

## 솔루션Solution

Xamarin.ios 10 및 Xamarin.ios 2.10에서는 관리 되는 네이티브 경계에서 관리 되는 및 목표 C 예외를 모두 catch 하 고 해당 예외를 다른 형식으로 변환 하기 위한 지원이 추가 되었습니다.In Xamarin.iOS 10 and Xamarin.Mac 2.10, we've added support for catching both managed and Objective-C exceptions on any managed-native boundary, and for converting that exception to the other type.

의사 코드에서 다음과 같이 표시 됩니다.In pseudo-code, it looks something like this:

[DllImport ("libobjc.dylib")]
static extern void objc_msgSend (IntPtr handle, IntPtr selector);

static void DoSomething (NSObject obj)
{
    objc_msgSend (obj.Handle, Selector.GetHandle ("doSomething"));
}

Objc_msgSend에 대 한 P/Invoke가 가로채 며 대신 호출 됩니다.The P/Invoke to objc_msgSend is intercepted, and this is called instead:

void
xamarin_dyn_objc_msgSend (id obj, SEL sel)
{
    @try {
        objc_msgSend (obj, sel);
    } @catch (NSException *ex) {
        convert_to_and_throw_managed_exception (ex);
    }
}

반대의 경우도 마찬가지입니다 (관리 되는 예외를 목표-C 예외로 마샬링).And something similar is done for the reverse case (marshaling managed exceptions to Objective-C exceptions).

관리 되는 네이티브 경계에서 예외를 catch 하는 것은 비용이 들지 않으므로 항상 기본적으로 사용 하도록 설정 되지 않습니다.Catching exceptions on the managed-native boundary is not cost-free, so it's not always enabled by default:

  • Xamarin.ios/tvOS: 시뮬레이터에서 목표-C 예외 가로채기를 사용할 수 있습니다.Xamarin.iOS/tvOS: interception of Objective-C exceptions is enabled in the simulator.
  • WatchOS: 가로채기는 모든 경우에 적용 됩니다 .이는 목표-C 런타임 해제 관리 되는 프레임을 사용 하 여 가비지 수집기를 혼동 하 고이를 중지 하거나 중단 시킬 수 있기 때문입니다.Xamarin.watchOS: interception is enforced in all cases, because letting the Objective-C runtime unwind managed frames will confuse the garbage collector, and either make it hang or crash.
  • Xamarin.ios: 디버그 빌드에는 목표-C 예외 가로채기를 사용할 수 있습니다.Xamarin.Mac: interception of Objective-C exceptions is enabled for debug builds.

빌드 타임 플래그 섹션에서는 기본적으로 사용 하도록 설정 되지 않은 경우 가로채기를 사용 하도록 설정 하는 방법을 설명 합니다.The Build-time flags section explains how to enable interception when it's not enabled by default.

이벤트Events

예외가 가로채 면 발생 하는 두 개의 새 이벤트 인 Runtime.MarshalManagedExceptionRuntime.MarshalObjectiveCException있습니다.There are two new events that are raised once an exception is intercepted: Runtime.MarshalManagedException and Runtime.MarshalObjectiveCException.

두 이벤트는 throw 된 원래 예외 (Exception 속성)를 포함 하는 EventArgs 개체와 예외가 마샬링되는 방법을 정의 하는 ExceptionMode 속성을 전달 합니다.Both events are passed an EventArgs object that contains the original exception that was thrown (the Exception property), and an ExceptionMode property to define how the exception should be marshaled.

처리기에서 수행 되는 사용자 지정 처리에 따라 동작을 변경 하려면 이벤트 처리기에서 ExceptionMode 속성을 변경할 수 있습니다.The ExceptionMode property can be changed in the event handler to change the behavior according to any custom processing done in the handler. 한 가지 예는 특정 예외가 발생 하는 경우 프로세스를 중단 하는 것입니다.One example would be to abort the process if a certain exception occurs.

ExceptionMode 속성을 변경 하는 것은 단일 이벤트에 적용 되 고 나중에 가로채는 예외에는 영향을 주지 않습니다.Changing the ExceptionMode property applies to the single event, it does not affect any exceptions intercepted in the future.

사용할 수 있는 모드는 다음과 같습니다.The following modes are available:

  • Default: 기본값은 플랫폼에 따라 다릅니다.Default: The default varies by platform. GC가 협조적 모드 (watchOS)에 있는 경우에는 ThrowObjectiveCException이 고, 그렇지 않으면 UnwindNativeCode (iOS/watchOS/macOS)입니다.It is ThrowObjectiveCException if the GC is in cooperative mode (watchOS), and UnwindNativeCode otherwise (iOS / watchOS / macOS). 기본값은 나중에 변경 될 수 있습니다.The default may change in the future.
  • UnwindNativeCode: 이전 (정의 되지 않은) 동작입니다.UnwindNativeCode: This is the previous (undefined) behavior. 협조적 모드로 GC를 사용 하는 경우에는이 옵션을 사용할 수 없습니다 .이 옵션은 watchOS의 유일한 옵션 이지만 watchOS에 대 한 올바른 옵션이 아니지만 다른 모든 플랫폼의 기본 옵션입니다.This is not available when using the GC in cooperative mode (which is the only option on watchOS; thus, this is not a valid option on watchOS), but it's the default option for all other platforms.
  • ThrowObjectiveCException: 관리 되는 예외를 목표-C 예외로 변환 하 고 목표-C 예외를 throw 합니다.ThrowObjectiveCException: Convert the managed exception into an Objective-C exception and throw the Objective-C exception. WatchOS에 대 한 기본값입니다.This is the default on watchOS.
  • Abort: 프로세스를 중단 합니다.Abort: Abort the process.
  • Disable: 예외 가로채기를 사용 하지 않도록 설정 하므로 이벤트 처리기에서이 값을 설정 하는 것은 적합 하지 않지만 이벤트가 발생 한 후에는이 값을 사용 하지 않도록 설정 하는 것이 너무 지연 됩니다.Disable: Disables the exception interception, so it doesn't make sense to set this value in the event handler, but once the event is raised it's too late to disable it. 어떤 경우 든, 설정 된 경우 UnwindNativeCode처럼 동작 합니다.In any case, if set, it will behave as UnwindNativeCode.

관리 코드에 대 한 마샬링 목표-C 예외에는 다음 모드를 사용할 수 있습니다.For marshaling Objective-C exceptions to managed code, the following modes are available:

  • Default: 기본값은 플랫폼에 따라 다릅니다.Default: The default varies by platform. GC가 협조적 모드 (watchOS)에 있는 경우에는 ThrowManagedException이 고, 그렇지 않으면 UnwindManagedCode (iOS/tvOS/macOS)입니다.It is ThrowManagedException if the GC is in cooperative mode (watchOS), and UnwindManagedCode otherwise (iOS / tvOS / macOS). 기본값은 나중에 변경 될 수 있습니다.The default may change in the future.
  • UnwindManagedCode: 이전 (정의 되지 않은) 동작입니다.UnwindManagedCode: This is the previous (undefined) behavior. 협조적 모드에서 GC를 사용 하는 경우에는이 옵션을 사용할 수 없습니다 .이는 watchOS에서 유일 하 게 유효한 GC 모드입니다 .이는 watchOS의 올바른 옵션이 아니지만 다른 모든 플랫폼의 기본값입니다.This is not available when using the GC in cooperative mode (which is the only valid GC mode on watchOS; thus this is not a valid option on watchOS), but it's the default for all other platforms.
  • ThrowManagedException: 목표-C 예외를 관리 되는 예외로 변환 하 고 관리 되는 예외를 throw 합니다.ThrowManagedException: Convert the Objective-C exception to a managed exception and throw the managed exception. WatchOS에 대 한 기본값입니다.This is the default on watchOS.
  • Abort: 프로세스를 중단 합니다.Abort: Abort the process.
  • Disable:D isables는 예외 가로채기를 가능 하 게 하므로 이벤트 처리기에서이 값을 설정 하는 것은 적합 하지 않지만 이벤트가 발생 한 후에는이 값을 사용 하지 않도록 설정 하는 것이 너무 늦습니다.Disable:Disables the exception interception, so it doesn't make sense to set this value in the event handler, but once the event is raised, it's too late to disable it. 설정 된 경우 어떤 경우 든 프로세스가 중단 됩니다.In any case if set, it will abort the process.

따라서 예외가 마샬링될 때마다 다음과 같은 작업을 수행할 수 있습니다.So, to see every time an exception is marshaled, you can do this:

Runtime.MarshalManagedException += (object sender, MarshalManagedExceptionEventArgs args) =>
{
    Console.WriteLine ("Marshaling managed exception");
    Console.WriteLine ("    Exception: {0}", args.Exception);
    Console.WriteLine ("    Mode: {0}", args.ExceptionMode);
    
};
Runtime.MarshalObjectiveCException += (object sender, MarshalObjectiveCExceptionEventArgs args) =>
{
    Console.WriteLine ("Marshaling Objective-C exception");
    Console.WriteLine ("    Exception: {0}", args.Exception);
    Console.WriteLine ("    Mode: {0}", args.ExceptionMode);
};

빌드 시간 플래그Build-Time Flags

다음 옵션을 mtouch (xamarin.ios 앱의 경우) 및 mmp (xamarin.ios 앱의 경우)에 전달 하 여 예외 가로채기를 사용 하도록 설정 했는지 여부를 확인 하 고 수행 해야 하는 기본 작업을 설정할 수 있습니다.It's possible to pass the following options to mtouch (for Xamarin.iOS apps) and mmp (for Xamarin.Mac apps), which will determine if exception interception is enabled, and set the default action that should occur:

  • --marshal-managed-exceptions=

    • default
    • unwindnativecode
    • throwobjectivecexception
    • abort
    • disable
  • --marshal-objectivec-exceptions=

    • default
    • unwindmanagedcode
    • throwmanagedexception
    • abort
    • disable

disable를 제외 하 고 이러한 값은 MarshalManagedExceptionMarshalObjectiveCException 이벤트에 전달 되는 ExceptionMode 값과 동일 합니다.Except for disable, these values are identical to the ExceptionMode values that are passed to the MarshalManagedException and MarshalObjectiveCException events.

disable 옵션은 대부분 의 경우에는 실행 오버 헤드를 추가 하지 않을 때 예외를 가로채는 점을 제외 하면 가로채기를 사용 하지 않도록 설정 합니다.The disable option will mostly disable interception, except we'll still intercept exceptions when it does not add any execution overhead. 마샬링 이벤트는 이러한 예외에 대해 계속 발생 하며 기본 모드는 실행 중인 플랫폼에 대 한 기본 모드입니다.The marshaling events are still raised for these exceptions, with the default mode being the default mode for the executing platform.

제한 사항Limitations

목표-C 예외를 catch 하려고 할 때 objc_msgSend 함수 제품군에 대 한 P/Invoke만 가로챕니다.We only intercept P/Invokes to the objc_msgSend family of functions when trying to catch Objective-C exceptions. 즉, 모든 목표-C 예외를 throw 하는 다른 C 함수에 대 한 P/Invoke는 여전히 이전 및 정의 되지 않은 동작으로 실행 됩니다 .이는 나중에 향상 될 수 있습니다.This means that a P/Invoke to another C function, which then throws any Objective-C exceptions, will still run into the old and undefined behavior (this may be improved in the future).