在 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 methodSignatureForSelector
或 does 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 查找你在当前程序中引用的方法的符号,而不是尝试从共享库加载它。