Unified API 概述
使用 Xamarin Unified API 可在 Mac 和 iOS 之间共享代码,该 API 还支持使用相同二进制文件的 32 和 64 位应用程序。 在新的 Xamarin.iOS 和 Xamarin.Mac 项目中会默认使用 Unified API。
重要
Xamarin Classic API(Unified API 的前身)已被弃用。
- 支持 Classic API (monotouch.dll) 的最后一个 Xamarin.iOS 版本是 Xamarin.iOS 9.10。
- Xamarin.Mac 仍然支持 Classic API,但不再更新。 由于 Classic API 已被弃用,开发人员应将其应用程序迁移到 Unified API。
更新基于 Classic API 的应用
按照平台的相关说明操作:
将代码更新为 Unified API 的提示
无论迁移哪些应用程序,都请查看这些提示,它们可帮助你成功更新到 Unified API。
库拆分
从现在开始,我们的 API 将以两种方式出现:
- Classic API:限制为 32 位(仅限),并在
monotouch.dll
和XamMac.dll
程序集中公开。 - Unified API:同时支持 32 位和 64 位开发,有单个 API 可在
Xamarin.iOS.dll
和Xamarin.Mac.dll
程序集中使用。
这意味着,对于企业开发人员(不面向 App Store),你可以继续使用现有的 Classic API,因为我们将永远维护它们,或者你可以升级到新的 API。
命名空间更改
为了更顺畅地在 Mac 和 iOS 产品之间共享代码,我们将更改产品中 API 的命名空间。
对于数据类型,我们将从 iOS 产品中删除“MonoTouch”前缀,从 Mac 产品中删除“MonoMac”。
这样,可以更轻松地在 Mac 和 iOS 平台之间共享代码,而无需进行条件编译,并减少源代码文件顶部的干扰。
- Classic API:命名空间使用
MonoTouch.
或MonoMac.
前缀。 - Unified API:无命名空间前缀
运行时默认值
Unified API 默认使用 SGen 垃圾回收器,并使用新的引用计数系统来跟踪对象所有权。 此功能已移植到 Xamarin.Mac。
这解决了开发人员使用旧系统时面临的诸多问题,还简化了内存管理。
请注意,即使对于 Classic API 也可启用新的 New Refcount,但默认值是保守的,不需要用户进行任何更改。 借助 Unified API,我们有机会来更改默认值,并在开发人员重构和重新测试其代码的同时为他们提供所有改进。
API 更改
Unified API 删除了已弃用的方法;在一些实例中,当 API 名称在 Classic API 中绑定到原始 MonoTouch 和 MonoMac 名称空间时,这些名称存在拼写错误。 这些实例已在新的 Unified API 中得到纠正,需要在组件、iOS 和 Mac 应用程序中更新。 下面是你可能会遇到的最常见名称的列表:
Classic API 方法名称 | Unified API 方法名称 |
---|---|
UINavigationController.PushViewControllerAnimated() |
UINavigationController.PushViewController() |
UINavigationController.PopViewControllerAnimated() |
UINavigationController.PopViewController() |
CGContext.SetRGBFillColor() |
CGContext.SetFillColor() |
NetworkReachability.SetCallback() |
NetworkReachability.SetNotification() |
CGContext.SetShadowWithColor |
CGContext.SetShadow |
UIView.StringSize |
UIKit.UIStringDrawing.StringSize |
有关在从 Classic API 切换到 Unified API 时出现的更改的完整列表,请参阅我们的 Classic API (monotouch.dll) 与 Unified API (Xamarin.iOS.dll) 文档。
更新到 Unified
Classic API 中的几个旧的/损坏/弃用的 API 在 Unified API 中不可用。 在开始(手动或自动)升级之前修复 CS0616
警告可能更容易,因为你将有 [Obsolete]
属性消息(警告的一部分)来知道你使用正确的 API。
请注意,我们将发布 Classic API 与 Unified API 变化的差异,你可在项目更新之前或更新后使用这些更改。 在 Classic 中修复过时的调用通常会节省时间(查找的文档更少)。
按照这些说明将现有 iOS 应用或 Mac 应用更新到 Unified API。 查看此页面的其余部分,并查看这些提示,详细了解如何迁移代码。
NuGet
以前通过 Classic API 支持 Xamarin.iOS 的 NuGet 包使用 Monotouch10 平台名字对象发布了其程序集。
Unified API 为兼容的包引入了新的平台标识符 - Xamarin.iOS10。 需要更新现有的 NuGet 包,以便根据 Unified API 进行构建来添加对此平台的支持。
重要
如果在将应用程序转换为 Unified API 之后收到以下形式的错误,这通常是因为项目中有组件或 NuGet 包尚未更新到 Unified API:“错误 3 无法在相同的 Xamarin.iOS 项目中同时包含 "monotouch.dll" 和 "Xamarin.iOS.dll" - "Xamarin.iOS.dll" 被显式引用,而 "monotouch.dll" 按 'xxx, Version=0.0.000, Culture=neutral, PublicKeyToken=null" 引用”。 需要删除现有组件/NuGet,更新到支持 Unified API 的版本并执行干净生成。
改为使用 64 位
有关支持 32 位和 64 位应用程序的背景信息和框架相关信息,请参阅 32 位和 64 位平台注意事项。
新数据类型
区别的核心在于,Mac API 和 iOS API 都使用特定于体系结构的数据类型,这些数据类型在 32 位平台上始终为 32 位,在 64 位平台上始终为 64 位。
例如,Objective-C 在 32 位系统上将 NSInteger
数据类型映射到 int32_t
,在 64 位系统上则映射到 int64_t
。
为了匹配此行为,在 Unified API 上,我们将以前使用的 int
(它在 .NET 中定义为始终是 System.Int32
)替换为新的数据类型:System.nint
。 可以将“n”理解为“native”(原生),也就是平台的原生整数类型。
我们将引入 nint
、nuint
和 nfloat
,并在必要时提供基于它们构建的数据类型。
若要详细了解这些数据类型的变化,请参阅原生类型文档。
如何检测 iOS 应用的体系结构
在某些情况下,应用程序需要知道它是在 32 位还是 64 位 iOS 系统上运行。 以下代码可用于检查体系结构:
if (IntPtr.Size == 4) {
Console.WriteLine ("32-bit App");
} else if (IntPtr.Size == 8) {
Console.WriteLine ("64-bit App");
}
数组和 System.Collections.Generic
由于 C# 索引器期望类型为 int
,因此必须将 nint
值显式强制转换为 int
才能访问集合或数组中的元素。 例如:
public List<string> Names = new List<string>();
...
public string GetName(nint index) {
return Names[(int)index];
}
这是预期行为,因为在 64 位系统上从 int
强制转换为 nint
是有损的,因此不会进行隐式转换。
将 DateTime 转换为 NSDate
使用 Unified API 时,不再执行从 DateTime
到 NSDate
值的隐式转换。 这些值需要从一种类型显式转换为另一种类型。 以下扩展方法可用于自动执行此过程:
public static DateTime NSDateToDateTime(this NSDate date)
{
// NSDate has a wider range than DateTime, so clip
// the converted date to DateTime.Min|MaxValue.
double secs = date.SecondsSinceReferenceDate;
if (secs < -63113904000)
return DateTime.MinValue;
if (secs > 252423993599)
return DateTime.MaxValue;
return (DateTime) date;
}
public static NSDate DateTimeToNSDate(this DateTime date)
{
if (date.Kind == DateTimeKind.Unspecified)
date = DateTime.SpecifyKind (date, /* DateTimeKind.Local or DateTimeKind.Utc, this depends on each app */)
return (NSDate) date;
}
已弃用的 API 和拼写错误
在 Xamarin.iOS Classic API (monotouch.dll) 中,[Obsolete]
属性以两种不同的方式使用:
- 已弃用的 iOS API:出现的情况是 Apple 提示你停止使用某个 API,因为它被更新的 API 取代。 Classic API 仍然很好,并且通常被需要(如果你支持较旧版本的 iOS)。
此类 API(和
[Obsolete]
属性)包含在新的 Xamarin.iOS 程序集中。 - 错误的 API:某些 API 的名称存在拼写错误。
对于原始程序集(monotouch.dll 和 XamMac.dll),我们保留了旧代码来确保兼容行,但是我们从 Unified API 程序集(Xamarin.iOS.dll 和 Xamarin.Mac)中删除了它们
NSObject 子类 .ctor(IntPtr)
每个 NSObject
子类都有一个接受 IntPtr
的构造函数。 这就是我们可以从本机 ObjC 句柄实例化新的托管实例的方式。
在 Classic 中,这是一个 public
构造函数。 但是,在用户代码中很容易滥用此功能,例如为单个 ObjC 实例创建多个托管实例,或者创建(对于子类而言)缺少预期托管状态的托管实例。
为了避免此类问题,IntPtr
构造函数在 Unified API 中接受 protected
,仅用于子类。 这将确保使用正确/安全的 API 从句柄创建托管实例,如下所示
var label = Runtime.GetNSObject<UILabel> (handle);
此 API 将返回现有托管实例(如果已存在),或者创建新的托管实例(如果需要)。 它已在 Classic API 和 Unified API 中提供。
请注意,.ctor(NSObjectFlag)
现在也接受 protected
,但它很少在子类之外使用。
NSAction 替换为 Action
使用 Unified API 时,NSAction
已删除,取而代之的是标准 .NET Action
。 这是一个很大的改进,因为 Action
是一种常见的 .NET 类型,而 NSAction
特定于 Xamarin.iOS。 它们的用途完全相同,但它们是不同且不兼容的类型,因此必须编写更多代码才能实现相同的结果。
例如,如果现有的 Xamarin 应用程序包含以下代码:
UITapGestureRecognizer singleTap = new UITapGestureRecognizer (new NSAction (delegate() {
ShowDropDownAnimated (tblDataView);
}));
现在可以将它替换为简单的 Lambda:
UITapGestureRecognizer singleTap = new UITapGestureRecognizer (() => ShowDropDownAnimated(tblDataView));
以前,这会出现编译器错误,因为无法将 Action
分配给 NSAction
,但是由于 UITapGestureRecognizer
现在采用 Action
而不是 NSAction
,它在 Unified API 中有效。
自定义委托替换为 Action<T>
在 Unified 中,一些简单的 .Net 委托(例如一个参数)被替换为 Action<T>
。 例如
public delegate void NSNotificationHandler (NSNotification notification);
现在可以用作 Action<NSNotification>
。 这可促进代码重用,并减少 Xamarin.iOS 和你自己的应用程序内的代码重复。
Task<bool> 替换为 Task<Boolean,NSError>>
在 Classic 中,有一些异步 API 返回 Task<bool>
。 但是,其中的一些是在 NSError
包含在签名中时使用的,在这种情况下,bool
已经为 true
,并且你必须捕获异常才能获取 NSError
。
由于某些错误很常见,并且返回值没有用处,所以此模式在 Unified 中更改,现在返回 Task<Tuple<Boolean,NSError>>
。 这样,你可以检查异步调用期间可能发生的成功和任何错误。
NSString 与 string
在少数情况下,某些常量必须从 string
更改为 NSString
(例如 UITableViewCell
)
经典
public virtual string ReuseIdentifier { get; }
Unified
public virtual NSString ReuseIdentifier { get; }
通常,我们更喜欢 .NET System.String
类型。 但是,尽管有 Apple 指南,但一些本机 API 会比较常量指针(而不是字符串本身),这只能在我们以 NSString
的形式公开常量时正常工作。
Objective-C 协议
原始 MonoTouch 不完全支持 ObjC 协议,我们添加了一些非最佳 API 来支持最常见的场景。 这个限制已经不存在了,但为了后向兼容性,在 monotouch.dll
和 XamMac.dll
中保留了几个 API。
在 Unified API 上删除并清理了这些限制。 大多数更改如下所示:
经典
public virtual AVAssetResourceLoaderDelegate Delegate { get; }
Unified
public virtual IAVAssetResourceLoaderDelegate Delegate { get; }
I
前缀意味着 Unified 公开一个接口(而不是特定类型)供 ObjC 协议使用。 这可简化你不希望将 Xamarin.iOS 提供的特定类型子类化的情况。
它还使得某些 API 更加精确且易于使用,例如:
经典
public virtual void SelectionDidChange (NSObject uiTextInput);
Unified
public virtual void SelectionDidChange (IUITextInput uiTextInput);
现在,无需参考文档即可更轻松地使用此类 API,而且 IDE 代码补全将基于协议/接口为你提供更有用的建议。
NSCoding 协议
我们的原始绑定为每种类型都包括一个 .ctor(NSCoder),即使它不支持 NSCoding
协议也是如此。 NSObject
中存在单个 Encode(NSCoder)
方法用来编码对象。
但是,只有当实例符合 NSCoding 协议时,此方法才起作用。
在 Unified API 上,我们解决了这个问题。 仅当类型符合 NSCoding
时,新的程序集才具有 .ctor(NSCoder)
。 此外,这些类型现在还有一个符合 INSCoding
接口的 Encode(NSCoder)
方法。
影响很小:在大多数情况下,此更改不会影响应用程序,因此无法使用旧的已删除的构造函数。
其他提示
有关需要注意的其他更改,可查看有关将应用更新到 Unified API 的提示。