为了控制我所开发的iOS游戏中动画之间的延迟,我编写了这个&;delayer &;下面的类。但是我遇到了一些罕见的随机崩溃,可能与对象被释放有关:
0x102933344 - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_dump_native_crash_info
0x1029295b0 - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_handle_native_crash
0x10293776c - /var/containers/Bundle/Application/52DE96A6-70CF-4D3D-A6F0-3DDAB4F31347/DiscDrop.iOS.app/DiscDrop.iOS : mono_sigsegv_signal_handler_debug
0x1dd9f69fc - /usr/lib/system/libsystem_platform.dylib : <redacted>
0x1dcfd9b9c - /usr/lib/libobjc.A.dylib : <redacted>
0x1dcfd9b9c - /usr/lib/libobjc.A.dylib : <redacted>
0x1dddf7bb0 - /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation : _CFAutoreleasePoolPop
0x1de871744 - /System/Library/Frameworks/Foundation.framework/Foundation : <redacted>
所以我想知道是否有人可以发现一个问题与类:
public class Delayer
{
private readonly List<CancellationTokenSource> cancellationTokenSources;
public Delayer()
{
this.cancellationTokenSources = new List<CancellationTokenSource>();
}
public void DelayedCall(float delay, Action callback)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
this.cancellationTokenSources.Add(cancellationTokenSource);
_ = Delayer.CallbackAfterDelay(delay, cancellationTokenSource, delegate
{
this.cancellationTokenSources.Remove(cancellationTokenSource);
callback?.Invoke();
});
}
public void CancelAll()
{
foreach (CancellationTokenSource cancellationTokenSource in this.cancellationTokenSources)
{
cancellationTokenSource.Cancel(); // cancellationTokenSource.Dispose(); here doesnt help leak
}
this.cancellationTokenSources.Clear();
}
private static async Task CallbackAfterDelay(float delay, CancellationTokenSource cancellationTokenSource, Action callback)
{
await Task.Delay((int)(delay * 1000), cancellationTokenSource.Token);
if (cancellationTokenSource.IsCancellationRequested)
{
return;
}
try
{
callback?.Invoke();
}
catch (Exception exception)
{
System.Diagnostics.Debug.WriteLine("### Common.Delayer.DelayedCall caught exception={0}", exception.Message);
throw;
}
}
}
这是我如何使用它的一个例子:
this.delayer = new Delayer();
//...
this.delayer.DelayedCall(delay: 0.5f, callback: delegate
{
this.PlaySound(duration: 0.2f);
}
_ = Delayer.CallbackAfterDelay
任务以"即发即弃"的方式启动,因此在实现中出现任何错误时不会通知您。并且我已经可以看到一个错误:await
的Task.Delay
可能导致未捕获的OperationCanceledException
,因此在这种情况下,callback?.Invoke();
将不会被调用,导致相关的cancellationTokenSource
不会从列表中删除。
确保所有异常将被显示的最简单方法是将CallbackAfterDelay
从async Task
转换为async void
。这个更改将迫使您在编写代码时必须非常小心,因为该方法中任何未被捕获的异常都将无法处理(而不仅仅是未被观察到),并且会导致进程崩溃。