Xamarin.iOS에서 UI 스레드 작업

애플리케이션 사용자 인터페이스는 다중 스레드 디바이스에서도 항상 단일 스레드입니다. 화면의 표현은 하나뿐이며 표시되는 내용에 대한 변경 내용은 단일 '액세스 지점'을 통해 조정되어야 합니다. 이렇게 하면 여러 스레드가 동일한 픽셀을 동시에 업데이트하지 못하게 됩니다(예: ).

코드는 기본(또는 UI) 스레드에서만 사용자 인터페이스 컨트롤을 변경해야 합니다. 다른 스레드(예: 콜백 또는 백그라운드 스레드)에서 발생하는 UI 업데이트는 화면에 렌더링되지 않거나 충돌이 발생할 수도 있습니다.

UI 스레드 실행

보기에서 컨트롤을 만들거나 터치와 같은 사용자 시작 이벤트를 처리하는 경우 코드는 이미 UI 스레드의 컨텍스트에서 실행되고 있습니다.

코드가 백그라운드 스레드, 태스크 또는 콜백에서 실행되는 경우 기본 UI 스레드에서 실행되지 않을 수 있습니다. 이 경우 다음과 같은 호출 InvokeOnMainThreadBeginInvokeOnMainThread 에서 코드를 래핑해야 합니다.

InvokeOnMainThread ( () => {
    // manipulate UI controls
});

InvokeOnMainThread 메서드는 뷰 또는 뷰 컨트롤러와 같은 UIKit 개체에 정의된 메서드 내에서 호출할 수 있도록 정의 NSObject 됩니다.

Xamarin.iOS 애플리케이션을 디버깅하는 동안 코드가 잘못된 스레드에서 UI 컨트롤에 액세스하려고 하면 오류가 발생합니다. 이렇게 하면 InvokeOnMainThread 메서드를 사용하여 이러한 문제를 추적하고 해결할 수 있습니다. 이는 디버깅하는 동안에만 발생하며 릴리스 빌드에서 오류를 throw하지 않습니다. 오류 메시지는 다음과 같이 표시됩니다.

UI Thread Execution

백그라운드 스레드 예제

다음은 간단한 스레드를 사용하여 백그라운드 스레드에서 사용자 인터페이스 컨트롤(a UILabel)에 액세스하려는 예제입니다.

new System.Threading.Thread(new System.Threading.ThreadStart(() => {
    label1.Text = "updated in thread"; // should NOT reference UILabel on background thread!
})).Start();

해당 코드는 디버깅하는 UIKitThreadAccessException 동안 throw됩니다. 문제를 해결하고 사용자 인터페이스 컨트롤이 기본 UI 스레드에서만 액세스되도록 하려면 다음과 같이 식 내에서 InvokeOnMainThread UI 컨트롤을 참조하는 코드를 래핑합니다.

new System.Threading.Thread(new System.Threading.ThreadStart(() => {
    InvokeOnMainThread (() => {
        label1.Text = "updated in thread"; // this works!
    });
})).Start();

이 문서의 예제를 다시 기본 데 사용할 필요는 없지만 앱이 네트워크 요청을 하거나 알림 센터 또는 다른 스레드에서 실행되는 완료 처리기가 필요한 다른 메서드를 사용할 때 기억해야 하는 중요한 개념입니다.

Async/Await 예제

대기 중인 작업이 완료되면 메서드가 호출 스레드에서 계속되므로 C# 5 비동기/대기 키워드(keyword) InvokeOnMainThread 사용할 필요가 없습니다.

이 예제 코드(지연 메서드 호출에서 대기하는 것은 순전히 데모용)는 UI 스레드에서 호출되는 비동기 메서드를 보여 줍니다(TouchUpInside 처리기임). 포함하는 메서드는 UI 스레드에서 호출되므로 백그라운드 스레드에서 UILabel 비동기 작업이 완료된 후 텍스트 설정 또는 표시 UIAlertView 와 같은 UI 작업을 안전하게 호출할 수 있습니다.

async partial void button2_TouchUpInside (UIButton sender)
{
    textfield1.ResignFirstResponder ();
    textfield2.ResignFirstResponder ();
    textview1.ResignFirstResponder ();
    label1.Text = "async method started";
    await Task.Delay(1000); // example purpose only
    label1.Text = "1 second passed";
    await Task.Delay(2000);
    label1.Text = "2 more seconds passed";
    await Task.Delay(1000);
    new UIAlertView("Async method complete", "This method", 
               null, "Cancel", null)
        .Show();
    label1.Text = "async method completed";
}

기본 UI 스레드 InvokeOnMainThread 가 아닌 백그라운드 스레드에서 비동기 메서드를 호출하는 경우에도 필요합니다.