2016-05-25 15 views
2

ViewDidLoadでビューモデルを初期化しようとしています。 ViewModelの初期化コードでいくつかの非同期メソッドを呼び出す必要があるので、非同期コードをコンストラクタからasync factory methodに移動しました。Xamarin Async ViewDidAppearがViewDidLoadで呼び出されました

私は私のUIViewControllerのサブクラスでasync voidとしてViewDidLoadViewWillAppearをマークしたが、4行目はViewWillAppearを実行している間に、何らかの理由でキックオフし、ライン11は、ViewModelには、まだ初期化されていないためNullReferenceExceptionスローされます。

Xamarinはであるため、ViewDidLoadが完了するのを待つことができませんが、ここではasync voidを使用する必要があります。これはメソッドをオーバーライドするためです。

MyCustomUiViewController.cs

1 public override async void ViewDidLoad() 
2 { 
3  base.ViewDidLoad(); 
4  ViewModel = await ViewModel.CreateAsync(); 
5  OtherStuff(); 
6 } 
7 
8 public override async void ViewWillAppear(bool animated) 
9 { 
10  base.ViewWillAppear(animated); 
11  ViewModel.SomeMethod(); // <-- NullReferenceException 
12  AttachViewModelToViewBindings(); 
13 } 

非同期のViewModelのインスタンスを作成するためのより良いパターンがある場合、私はアーキテクチャの変更に開いています。

+2

機能を非同期に設定しましたが、それでも待っているとは限りません。もしそれが、それは確かに、しかし、おそらくか? – bodangly

+0

私も同じ問題に直面していますが、回避策を見つけましたか? –

+0

@ ZeaShah私は以下の回答を追加しました。 – Seafish

答えて

3

ここでは、(抽出された使用一般的なパターンがありますthis gistに)。これにより、AsyncInitializationControllerを継承し、次にオーバーライドするコントローラを作成できます(たとえば、ViewDidLoadAsync)。コードは、後続の各ライフサイクルメソッドが前のライフサイクルメソッドを完了するのを待つように構成されています。

私たちは非同期ViewDidDisappearの必要はありませんでしたが、私はあなたもこのパターンにそれを動作させることができると確信しています。

using System; 
using System.Threading.Tasks; 
using UIKit; 

namespace Seanfisher.Gists 
{ 
    public abstract class AsyncInitializationController : UIViewController 
    { 
     Task _viewDidLoadAsyncTask = Task.CompletedTask; 
     public virtual Task ViewDidLoadAsync() 
     { 
      return _viewDidLoadAsyncTask; 
     } 

     public sealed override async void ViewDidLoad() 
     { 
      try 
      { 
       base.ViewDidLoad(); 
       _viewDidLoadAsyncTask = ViewDidLoadAsync(); 
       await _viewDidLoadAsyncTask; 
      } 
      catch (Exception e) 
      { 
       // Handle 
      } 
     } 

     Task _viewWillAppearAsyncTask = Task.CompletedTask; 
     public virtual Task ViewWillAppearAsync() 
     { 
      return _viewWillAppearAsyncTask; 
     } 

     public sealed override async void ViewWillAppear(bool animated) 
     { 
      try 
      { 
       await _viewDidLoadAsyncTask; 
       base.ViewWillAppear(animated); 
       _viewWillAppearAsyncTask = ViewWillAppearAsync(); 
       await _viewWillAppearAsyncTask; 
      } 
      catch (Exception e) 
      { 
       // Handle 
      } 
     } 

     Task _viewDidAppearAsyncTask = Task.CompletedTask; 
     public virtual Task ViewDidAppearAsync() 
     { 
      return _viewDidAppearAsyncTask; 
     } 
     public sealed override async void ViewDidAppear(bool animated) 
     { 
      try 
      { 
       await _viewDidLoadAsyncTask; 
       await _viewWillAppearAsyncTask; 

       base.ViewDidAppear(animated); 
       _viewDidAppearAsyncTask = ViewDidAppearAsync(); 
       await _viewDidAppearAsyncTask; 
      } 
      catch (Exception e) 
      { 
       // Handle 
      } 
     } 
    } 
} 
-2

Bodanglyが正しいです。

これらのメソッドは、非同期にマークするだけで非同期に呼び出されることはありません。

さらに、 "async void"は避けてください。これを読む:これを解決するhttps://msdn.microsoft.com/en-us/magazine/jj991977.aspx

より良いパターンがここで説明されています。このようなものでなければなりません http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

:(未テスト)

public override void ViewDidLoad() 
{ 
    base.ViewDidLoad(); 
    Initialization = InitializeAsync(); 

    OtherStuff(); 
} 

public Task Initialization { get; private set; } 

private async Task InitializeAsync() 
{ 
    // Do our own initialization (synchronous or asynchronous). 
    await Task.Delay(100); 
} 
+0

「非同期無効」は避けてください。この場合、非同期ボイドは避けられません。あなたがリンクしている記事から:「無効復帰非同期メソッドには、非同期イベントハンドラを可能にするという特定の目的があります。 "非同期voidメソッドにはいくつかの短所[...]がありますが、特定のケースでは非常に便利です:非同期イベントハンドラです。" ViewDidLoadは非同期イベントハンドラです。問題は、2つの非同期イベントハンドラが連続して呼び出され、2つ目の非同期イベントハンドラが最初に完了したことに依存する場合です。 – Seafish

関連する問題