Xamarin.iOS 中的新引用计数系统

默认情况下,Xamarin.iOS 9.2.1 向所有应用程序引入了增强型引用计数系统。 该系统可用于消除许多在早期版本的 Xamarin.iOS 中难以跟踪和修复的内存问题。

启用新的引用计数系统

从 Xamarin 9.2.1 开始,默认为所有应用程序启用新的引用计数系统。

如果要开发现有应用程序,可以检查 .csproj 文件,以确保出现的所有 MtouchUseRefCounting 都设置为 true,如下所示:

<MtouchUseRefCounting>true</MtouchUseRefCounting>

如果其设置为 false,则应用程序不会使用这一新工具。

使用旧版 Xamarin

Xamarin.iOS 7.2.1 及更高版本提供了新引用计数系统的增强预览。

Classic API:

若要启用此新的引用计数系统,请在项目的“iOS 生成”选项的“高级”选项卡中选中“使用引用计数扩展”复选框,如下所示:

Enable the new Reference Counting System

请注意,这些选项已在较新版本的 Visual Studio for Mac 中移除。

Unified API:

Unified API 需要新的引用计数扩展,且该扩展默认情况下应启用。 早期版本的 IDE 可能不会自动检查此值,因此你可能需要自己进行检查。

重要

该功能的早期版本自 MonoTouch 5.2 以来就一直存在,但仅作为实验预览版向 sgen 提供。 这个新的增强版本现在也可用于 Boehm 垃圾回收器

从历史上看,Xamarin.iOS 管理的对象有两种:一种是仅围绕本机对象的包装器(对等对象),另一种是扩展或合并新功能(通常是通过保留额外的内存状态)的对象(派生对象)。 以前,我们可以使用状态来扩充对等对象(例如添加 C# 事件处理程序),但允许对象不被引用,然后进行收集。 这可能会在以后导致崩溃(例如,如果 Objective-C 运行时回调到托管对象)。

当对等对象存储有任何额外信息时,新系统会自动将其升级为由运行时管理的对象。

这解决了在诸如以下情况下发生的各种崩溃:

class MyTableSource : UITableViewSource {
   public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath) {
        var cell = tableView.DequeueReusableCell ("myId");
        if (cell != null)
                return cell;

        cell = new UITableViewCell (UITableViewCellStyle.Default, "myId");
        var txt = new UITextField ();
        txt.TouchDown += delegate { Console.WriteLine ("...."); };
        cell.ContentView.AddSubview (txt);
        return cell;
   }
}

如果没有引用计数扩展,这段代码将崩溃,因为 cell 变得可收集,其 TouchDown 委托也将如此,而该委托将转换为悬空指针。

引用计数扩展可确保托管对象保持活动状态并阻止其收集,前提是本机对象由本机代码保留。

新系统还不需要绑定中使用的大多数专用支持字段,这是使实例保持活动状态的默认方法托管链接器足够智能,可以使用新的引用计数扩展从应用程序中移除所有这些不需要的字段。

这意味着每个托管对象实例消耗的内存都要比以前少。 它还解决了一个相关问题,即某些支持字段将保存 Objective-C 运行时不再需要的引用,因此很难回收一些内存。