我遇到了 MonoTouch 的问题,其中 UIViewControllers 永远保留在内存中,即使它们已从导航堆栈中弹出。
我有一个 UINavigationController,其中包含一个带有按钮的 UIViewController。单击该按钮会将名为 ThreadingViewController 的自定义 UIViewController 推送到导航堆栈上。
ThreadingViewController 使用 NSTimer.CreateRepeatingScheduledTimer 和 Thread 每隔一秒更新标签的文本。
当用户单击“返回”以弹回根视图时,Mono Profiler 说我的 ThreadingViewController 仍然存在于内存中。Profiler 告诉我它与 NSAction 和/或 ThreadStart 有关,后者引用了 ThreadingViewController,使其保持活动状态。我可以通过检查分析器中的“反向引用”复选框来看到这一点。
这意味着如果用户在根 ViewController 和自定义的 ThreadingViewController 之间来回点击 100 次,那么内存中将有 100 个该 ViewController 的实例。它没有被垃圾收集。
在 ViewDidDisappear 中,我尝试中止线程,将其设置为 null,但无济于事。
我需要做什么才能让这个 ThreadingViewController 被 MonoTouch 正确清理/GC?
这是重现该问题的完整(仅限 C#)源代码:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Threading;
namespace MemoryTests
{
[Register("AppDelegate")]
public partial class AppDelegate : UIApplicationDelegate
{
private UIWindow window;
private UINavigationController rootNavigationController;
private RootScreen rootScreen;
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
window = new UIWindow(UIScreen.MainScreen.Bounds);
rootScreen = new RootScreen();
rootNavigationController = new UINavigationController(rootScreen);
window.RootViewController = rootNavigationController;
window.MakeKeyAndVisible();
return true;
}
}
public class RootScreen : UIViewController
{
private UIButton button;
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.Title = "Root Screen";
// Add a button
button = new UIButton(UIButtonType.RoundedRect);
button.SetTitle("Click me", UIControlState.Normal);
button.Frame = new RectangleF(100, 100, 120, 44);
this.View.Add(button);
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
button.TouchUpInside += PushThreadingViewController;
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
button.TouchUpInside -= PushThreadingViewController;
}
private void PushThreadingViewController(object sender, EventArgs e)
{
var threadingViewController = new ThreadingViewController();
NavigationController.PushViewController(threadingViewController, true);
}
}
public class ThreadingViewController : UIViewController
{
private UILabel label;
private NSTimer timer;
private int counter;
public override void ViewDidLoad()
{
base.ViewDidLoad();
this.Title = "Threading Screen";
// Add a label
label = new UILabel();
label.Frame = new RectangleF(0f, 200f, 320f, 44f);
label.Text = "Count: 0";
this.View.Add(label);
// Start a timer
var timerThread = new Thread(StartTimer as ThreadStart);
timerThread.Start();
}
public override void ViewDidDisappear (bool animated)
{
base.ViewDidDisappear(animated);
timer.Dispose();
timer = null;
// Do I need to clean up more Threading things here?
}
[Export("StartTimer")]
private void StartTimer()
{
using (var pool = new NSAutoreleasePool())
{
timer = NSTimer.CreateRepeatingScheduledTimer(1d, TimerTicked);
NSRunLoop.Current.Run();
}
}
private void TimerTicked()
{
InvokeOnMainThread(() => {
label.Text = "Count: " + counter;
counter++;
});
}
}
}
这是分析器的屏幕截图,告诉我我们在内存中有 3 个 ThreadingViewController 实例:
干杯。