在 Xamarin.iOS 中引用本机库

Xamarin.iOS 支持使用本机 C 库和 Objective-C 库进行链接。 本文档讨论如何将本机 C 库与 Xamarin.iOS 项目链接。 有关为 Objective-C 库执行相同操作的信息,请参阅绑定 Objective-C 类型文档。

生成通用本机库(i386、ARMv7 和 ARM64)

通常最好为 iOS 开发支持的每个平台生成本机库(模拟器使用 i386,设备本身使用 ARMv7/ARM64)。 如果你的库已有 Xcode 项目,那么此操作就非常简单了。

若要生成本机库的 i386 版本,请从终端运行以下命令:

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphonesimulator -arch i386 -configuration Release clean build

这将使得本机静态库位于 MyProject.xcodeproj/build/Release-iphonesimulator/ 下。 将库存档文件 (libMyLibrary.a) 复制(或移动)到某个安全位置供以后使用,为其提供唯一名称(如 libMyLibrary-i386.a),以便它不会与接下来要生成的同一库的 arm64 和 armv7 版本发生冲突。

若要生成本机库的 ARM64 版本,请运行以下命令:

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphoneos -arch arm64 -configuration Release clean build

这一次,生成的本机库将位于 MyProject.xcodeproj/build/Release-iphoneos/。 再次将该文件复制(或移动)到安全位置,将其重命名为 libMyLibrary-arm64.a 之类的名字,使它不会发生冲突。

现在生成库的 ARMv7 版本:

/Developer/usr/bin/xcodebuild -project MyProject.xcodeproj -target MyLibrary -sdk iphoneos -arch armv7 -configuration Release clean build

将生成的库文件复制(或移动)到你将其他 2 个版本的库移到的同一位置,将其重命名为 libMyLibrary-armv7.a 之类的名字。

若要制作通用二进制文件,可以使用 lipo 工具,例如:

lipo -create -output libMyLibrary.a libMyLibrary-i386.a libMyLibrary-arm64.a libMyLibrary-armv7.a

这将创建 libMyLibrary.a,它会是一个通用 (fat) 库,该库将适合用于所有 iOS 开发目标。

缺少必需的体系结构 i386

如果当你尝试在 iOS 模拟器中使用 Objective-C 库时在运行时输出中收到了 does not implement methodSignatureForSelectordoes not implement doesNotRecognizeSelector 消息,则你的库可能未针对 i386 体系结构进行编译(请参阅上面的“生成通用本机库”部分)。

若要检查给定库支持的体系结构,请在终端中使用以下命令:

lipo -info /full/path/to/libraryname.a

其中 /full/path/to/ 是正在使用的库的完整路径,libraryname.a 是相关库的名称。

如果你有库的源,则还需要为 i386 体系结构编译并捆绑它(如果要在 iOS 模拟器上测试应用)。

链接库

你使用的任何第三方库都需要与应用程序静态链接。

如果想要静态链接从 Internet 获取或使用 Xcode 生成的库“libMyLibrary.a”,则需要执行一些操作:

  • 将库引入项目
  • 配置 Xamarin.iOS 以链接库
  • 从库访问方法。

若要将库引入项目,请从解决方案资源管理器中选择项目,然后按 Command+Option+a。 导航到 libMyLibrary.a 并将其添加到项目。 出现提示时,告知 Visual Studio for Mac 或 Visual Studio 将其复制到项目中。 添加后,在项目中找到 libFoo.a,右键单击它,然后将“生成操作”设置为 “无”。

若要配置 Xamarin.iOS 以链接库,则需要在最终可执行文件(不是库本身,而是最终程序)的项目选项上添加 iOS Build 的额外参数(这些是项目选项的一部分)“-gcc_flags”选项,后跟包含你的程序所需的所有额外库的字符串,例如:

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

上面的示例将链接 libMyLibrary.a

可使用 -gcc_flags 指定任何命令行参数集,以传递给用于执行可执行文件的最终链接的 GCC 编译器。 例如,此命令行还引用 CFNetwork 框架:

-gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -lSystemLibrary -framework CFNetwork -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

如果你的本机库包含 C++ 代码,则还必须在“额外参数”中传递 -cxx 标志,以便 Xamarin.iOS 知道使用正确的编译器。 对于 C++,上述选项如下所示:

-cxx -gcc_flags "-L$(MSBuildProjectDirectory) -lMylibrary -lSystemLibrary -framework CFNetwork -force_load $(MSBuildProjectDirectory)/libMyLibrary.a"

从 C# 访问 C 方法

iOS 上提供了两种类型的本机库:

  • 属于操作系统的共享库。

  • 应用程序随附的静态库。

若要访问其中任意一个中定义的方法,请使用 Mono 的 P/Invoke 功能,该功能与在 .NET 中使用的技术相同,大致如下:

  • 确定要调用的 C 函数
  • 确定其签名
  • 确定它所在的库
  • 编写相应的 P/Invoke 声明

使用 P/Invoke 时,需要指定要链接到的库的路径。 使用 iOS 共享库时,可以硬编码路径,也可以使用我们在我们的 Constants 中定义的便利常量,这些常量应覆盖 iOS 共享库。

例如,如果要从 Apple 的 UIKit 库调用 UIRectFrameUsingBlendMode 方法,该库在 C 中具有此签名:

void UIRectFrameUsingBlendMode (CGRect rect, CGBlendMode mode);

P/Invoke 声明将如下所示:

[DllImport (Constants.UIKitLibrary,EntryPoint="UIRectFrameUsingBlendMode")]
public extern static void RectFrameUsingBlendMode (RectangleF rect, CGBlendMode blendMode);

Constants.UIKitLibrary 只是一个定义为“/System/Library/Frameworks/UIKit.framework/UIKit”的常量,EntryPoint 让我们可以选择性地指定外部名称 (UIRectFramUsingBlendMode),同时在 C# 中公开其他名称(较短的 RectFrameUsingBlendMode)。

访问 C Dylibs

如果需要在 Xamarin.iOS 应用程序中使用 C Dylib,则调用 DllImport 特性之前需要一些额外的设置。

例如,如果我们有一个具有 Animal_Version 方法的 Animal.dylib,且在应用程序中将调用它,则需要在尝试使用该库之前告知 Xamarin.iOS 它的位置。

为此,请编辑 Main.CS 文件,使其如下所示:

static void Main (string[] args)
{
    // Load Dylib
    MonoTouch.ObjCRuntime.Dlfcn.dlopen ("/full/path/to/Animal.dylib", 0);

    // Start application
    UIApplication.Main (args, null, "AppDelegate");
}

其中 /full/path/to/ 是正在使用的 Dylib 的完整路径。 此代码就位后,可以按如下所示链接到 Animal_Version 方法:

[DllImport("Animal.dylib", EntryPoint="Animal_Version")]
public static extern double AnimalLibraryVersion();

静态库

由于只能在 iOS 上使用静态库,因此没有要与之链接的外部共享库,因此 DllImport 特性中的路径参数需要使用特殊名称 __Internal(注意名称开头的双下划线字符),而不是路径名称。

这会强制 DllImport 查找你在当前程序中引用的方法的符号,而不是尝试从共享库加载它。