在ViewDidLoad期间调用Xamarin Async ViewDidAppear

时间:2016-05-25 23:21:43

标签: c# .net xamarin xamarin.ios async-await

我试图在ViewDidLoad上初始化视图模型。我需要在ViewModel初始化代码中调用一些异步方法,因此我已将异步代码从构造函数中移到async factory method

我已在我的UIViewController子类中将ViewDidLoadViewWillAppear标记为async void,但由于某些原因,当第4行正在执行时,ViewWillAppear已启动第11行抛出NullReferenceException因为ViewModel尚未初始化。

我怀疑Xamarin不能等待ViewDidLoad完成,因为它是async void,但我必须在这里使用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 个答案:

答案 0 :(得分:4)

这是我们使用的广义模式(提取到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
            }
        }
    }
}

答案 1 :(得分:-2)

Bodangly是对的。

这些方法不会被称为asynchrounsly只是因为你将它们标记为异步。

此外 - 应始终避免“异步空白”。阅读: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);
}