Xamarin Async ViewDidAppear вызывается во время загрузки ViewDidLoad
Я пытаюсь инициализировать модель представления на ViewDidLoad. Мне нужно вызвать некоторые асинхронные методы в коде инициализации ViewModel, поэтому я переместил асинхронный код из конструктора васинхронный фабричный метод .
Я пометил ViewDidLoad
и ViewWillAppear
как async void
в моем подклассе UIViewController, но по какой-то причине, пока строка 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 ответа:
Вот обобщенный паттерн, который мы использовали (извлеченный в эту суть). Это позволяет создать контроллер, который наследует от
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 } } } }
Бодангли прав.
Методы не будут называться асинхронными только потому, что вы отмечаете их как асинхронные.
Кроме того, всегда следует избегать "асинхронной пустоты". Прочтите это: 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); }