2016-05-25 23 views
2

Ich versuche, ein Ansichtsmodell auf ViewDidLoad zu initialisieren. Ich muss einige Async-Methoden im ViewModel-Initialisierungscode aufrufen, daher habe ich den Async-Code aus dem Konstruktor in eine async factory method verschoben.Xamarin Async ViewDidAppear aufgerufen während ViewDidLoad

Ich habe die ViewDidLoad und ViewWillAppear als async void in meiner UIViewController Unterklasse markiert, aber aus irgendeinem Grunde, während der Linie 4 ist die ViewWillAppear ausgeführt wird gekickt und Linie 11 führt ein NullReferenceException weil das Ansichtsmodell noch nicht initialisiert ist.

Mein Verdacht ist, dass Xamarin kann nicht warten, bis ViewDidLoad abgeschlossen ist, weil es async void ist, aber ich muss eine async void hier verwenden, weil es eine Methode überschreibt.

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 } 

Ich bin offen, die Architektur zu ändern, wenn es ein besseres Muster für Instanziieren ein asynchrones Ansichtsmodelles ist.

+2

Sie haben die Funktionen async markiert, aber das heißt nicht, dass sie jemals erwartet werden. Nicht sicher, ob das so ist, aber vielleicht? – bodangly

+0

Ich bin auch mit dem gleichen Problem konfrontiert, haben Sie einen Workaround dafür gefunden? –

+0

@ZeaShah Ich habe unten eine Antwort hinzugefügt, die helfen kann – Seafish

Antwort

3

Hier ist die verallgemeinerte Muster, das wir verwendet (extrahiert in this gist). Auf diese Weise können Sie einen Controller erstellen, der von erbt und dann z. B. ViewDidLoadAsync überschreibt. Der Code ist so strukturiert, dass jede nachfolgende Lebenszyklusmethode auf die vorherigen wartet.

Während wir keine Notwendigkeit für eine asynchrone ViewDidDisappear hatten, bin ich sicher, dass Sie das auch in diesem Muster arbeiten könnten.

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 ist richtig.

Die Methoden werden nicht asynchron aufgerufen, nur weil Sie sie als async markieren.

Desweiteren sollte "async void" immer vermieden werden. Lesen Sie dazu: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Ein besseres Muster dieses Problem zu lösen wird hier erklärt: http://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

die so etwas wie das sein sollte: (Ungeprüfte)

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

"async void sollte ** immer ** vermieden werden" ist ein wenig zu stark. In diesem Fall ist Async-Void unumgänglich. Aus dem Artikel, mit dem Sie verlinkt haben: "Asynchrone Methoden, die eine Leerstelle zurückgeben, haben einen bestimmten Zweck: asynchrone Ereignisbehandlungsroutinen zu ermöglichen." und "Es ist klar, dass asynchrone void-Methoden mehrere Nachteile haben, aber sie sind in einem bestimmten Fall sehr nützlich: asynchrone Event-Handler." Genau das ist hier der Fall - ViewDidLoad ist ein asynchroner Event-Handler. Das Problem tritt auf, wenn zwei asynchrone Ereignisbehandlungsroutinen nacheinander aufgerufen werden und die zweite davon abhängig ist, ob die erste beendet wird. – Seafish

Verwandte Themen