【问题标题】:Xamarin Async ViewDidAppear called during ViewDidLoad在 ViewDidLoad 期间调用 Xamarin 异步 ViewDidAppear
【发布时间】:2016-09-23 18:27:16
【问题描述】:

我正在尝试在 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,我愿意更改架构。

【问题讨论】:

  • 您将函数标记为异步,但这并不意味着它们会被等待。不确定是不是这样,但也许?
  • 我也遇到了同样的问题,请问您找到解决方法了吗?
  • @ZeaShah 我在下面添加了一个可能有帮助的答案

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


【解决方案1】:

这是我们使用的通用模式(提取到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】:

    博当利是对的。

    这些方法不会因为您将它们标记为异步而被异步调用。

    此外 - 应始终避免使用“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);
    }
    

    【讨论】:

    • "async void 应该总是被避免"有点太强了。在这种情况下async void 是不可避免的。从您链接到的文章中:“返回无效的异步方法具有特定目的:使异步事件处理程序成为可能。” “很明显,async void 方法有几个缺点 [...],但它们在一种特殊情况下非常有用:异步事件处理程序。”这正是这里的情况 - ViewDidLoad 是一个异步事件处理程序。问题是当两个异步事件处理程序被连续调用并且第二个取决于第一个被完成时。
    猜你喜欢
    • 1970-01-01
    • 2017-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 2014-04-08
    • 1970-01-01
    相关资源
    最近更新 更多