How to create a Framework for iOS (一)创建静态库

原文地址

上一篇教程里,你学到了如何创建一个可复用的按钮控件。然而,对于开发者如何简单方便地来复用的话效果还不是非常明显。

一种共享它的方式就是直接提供源码文件。然而,这不是特别优雅。有可能你不想共享代码的实现细节。此外,开发者可能不想看见所有的东西,因为它们只是想继承一部分代码到自己的代码库里。

另一种方式是把你的代码编译成静态库来让开发者添加到他们的项目中去。然而这要求你来提供公共头文件,这样显得非常的笨拙。

你需要有一种简洁的方式来编译你的代码,并且它还要方便的共享和在多个项目间复用。你需要用什么方式来打包静态库并且它的头文件在一个文件里,然后只需要把这个文件添加到工程里就能立即开始使用。

好消息是这篇教程就是围绕这展开的。通过制作 framework,你会学到它能够帮你解决这些迫在眉睫的问题。OS X 对制作 framework 有着最好的支持,因为 Xcode 提供了一个工程模板,它包含有一个默认的构建目标还可以容纳资源文件,例如图片,声音和字体。你能够为 iOS 创建一个 framework,但这有点棘手,如果你跟着我一步一步来,你将会学到如何解决这些阻碍。

通过本教程的任务,你将:

  • 用 Xcode 构建一个基本静态库工程
  • 构建一个依赖与这个静态库工程的 app
  • 探索如何把静态库转换成一个完整的 framework
  • 最后,你将会看到如何把一个图片文件打包到 framework 中的资源包中

现在开始

这篇教程的主要目的是解释如何在 iOS 项目中创建一个可复用 framework,因此不会像本站中其他教程一样,而只会有少量的 Objective-C 代码,而且这些代码只是用来证明讲到的概念。

这里可以先下载好 RWKnobControl 资源文件。当你在 创建静态库项目 这个部分里创建第一个工程的过程中,你会看到如何使用它们。

你要创建的所有的这些代码和工程文件在 Github 上都能访问到,并且还有每个构建部分的独立提交。

什么是 Framework?

Framework 是一种资源集合,它把一个静态库和它的头文件汇集成一个单一的结构,这样 Xcode 能够很容易的合并到你的工程中去。

在 OS X 中,能够创建一个动态链接库。通过动态链接,framework 能够显性的实时更新而不需要应用程序重新链接它们。在运行时状态,一个单一的二进制库代码的副本在所有处理过程之间共享利用,因此这能够减少内存用量并提升系统的性能。正如你所看到的,这真是个强大的东西。

在 iOS 中,你不能用这种方式给系统添加自定义的 framework,因此只有 Apple 提供的动态链接库才行。

然而,这不意味着 framework 跟 iOS 就毫无相关了。对于在不同 app 中进行复用,静态链接库仍然是一个便捷的打包代码库的方式。

既然 framework 本质上是对静态库的一站式购物,那这篇教程中首要事情你了解如何创建和使用静态库。当这篇教程进展到构建 framework 时,你会知道接下来会发生什么。

创建静态库工程

打开 Xcode 并且通过点击 File\New\ProjectiOS\Framework and Library\Cocoa Touch Static Library 来创建一个新的静态库工程。

ios_framework_creating_static_lib

把工程命名为 RWUIControls 并且保存工程到一个空目录。

ios_framework_options_for_static_lib

一个静态库工程由头文件和实现文件组成,它们由工程自己创建编译。

为了让开发者更方便的使用你的库和框架,你需要导入一个头文件来访问所有的你希望公开的类,好让他们只需访问这个头文件就行。

当创建静态库工程的时候,Xcode 添加了 RWUIControls.hRWUIControls.m。你不需要实现文件,因此右键 RWUIControls.m 选择删除,按提示把它移到垃圾箱中。

打开 RWUIControls.h 并且用下面的代码替换文件内容:

#import <UIKit/UIKit.h>

这句代码导入了 UIKit 的伞型头文件,它包含有自身所需要的库。当你创建不同的组件类时,你要把它们添加到这个文件里,这样能够确保它们让这个库的使用者能访问。

你构建这个工程时会依赖 UIKit,但 Xcode 静态库工程没有默认的链接到 UIkit。为了修正这个问题,要添加 UIKit 作为一个依赖。选择工程的导航器,并且在主面板选择 RWUIControls 目标。

Click on Build Phases and then expand the Link Binary with Libraries section. Click the + to add a new framework and navigate to find UIKit.framework, before clicking add.
单击 Build Phases 然后展开 Link Binary With Libraries 部分。单击 + 来添加一个新的框架,查找 UIKit.framework,单击 add 添加。

ios_framework_add_uikit_dependency

RWUIControls.h 从导航器拖到面板的 Public 部分。这确保这个头文件对任何使用你库的用户都可用。

ios_framework_add_header_to_public

注意:这可能有点多此一举,但把包含有你工程所有公开类头文件的头文件放到公有部分非常重要。否则,开发者在企图使用这个库的时候会发生编译错误。这对任何人都不是开玩笑的,当 Xcode 读取公有头的时又不能读取你忘记添加的公有文件。

创建一个 UI 控件

现在你已经设置好了你的工程,是时候给库添加些功能了。既然这个教程的目的是讲诉如何构建一个 framework,而不是如何构建一个 UI 控件,那你会借用些上篇教程的一些代码。在你之前下载的 zip 文件你会找到 RWKnobControl 目录。把它拖到 Xcode 的 RWUIControls 组别。

ios_framework_drop_rwuiknobcontrol_from_finder

选择 Copy items into destination group’s folder 并确保要拷贝的新文件勾选了响应的单选框。

ios_framework_import_settings_for_rwknobcontrol

这会同时把实现文件添加到编译列表,默认的头文件在 Project group。这意味着它们都是私有的。

ios_framework_default_header_membership

注意:这三个部分的命名如果不拆分开理解会有点令人误解。Public 如你所预料的。Private 头仍然会暴露你的头文件,这有点让人困惑。Project 头是你工程用到的特定私有文件,这有点讽刺。因此,你会慢慢发现要么头文件是放到 Public 要么放在 Project 部分。

ios_framework_drag_header_to_public

另一种方式,当你编辑文件的时候会发现更改 Target Membership 面板中的值会更方便。当你开发库继续添加文件的时候这会非常方便。

ios_framework_header_membership

注意:在你往库中添加新的类时,记得保持成员是最新的。尽可能减少公有的头文件,并确保其余的在 Project 组。

用控件的头文件做的另一件事就是把 RWUIControls.h 它添加到库的主头文件中。这样开发者使用你的库时只需要像下面这样包含这一个文件就行,而不是一堆。

#import <RWUIControls/RWUIControls.h>

因此,把下面的代码添加到 RWUIControls.h

// Knob Control
#import <RWUIControls/RWKnobControl.h>

配置 Build 设置

现在你非常接近这个工程的编译部分了。然而,有几个确保库尽可能对用户友好的设置需要配置。

首先,你需要提供一个目录名给你公有头文件将要拷贝到那里去。这确保当你使用静态库的时候能定位到相关的头文件。

单击工程导航栏的工程,然后选择 RWUIControls 静态库目标。选择 Build Setting 标签,然后搜索 public header。双击 Public Header Folder Path 设置并输入下面的路径:

include/$(PROJECT_NAME)  

ios_framework_public_headers_path

之后你会看到这个目录。

现在你需要改变一些其他的设置,尤其是那些保留在二进制库中的。编译器给了你移除无用代码的选项,指那些从不会访问到的代码。并且你还能移除 debug 符号,例如函数名和其他 debug 时相关的细节。

既然你创建 framework 给其他人使用,那最好把它们都禁用了然后让用户自行选择最适合他们工程的配置。要做这些的话,跟之前一样使用搜索就行,更新下面的设置:

  • Dead Code Stripping – 设为 NO
  • Strip Debug Symbols During Copy – 设为 NO for all configurations
  • Strip Style – 设为 Non-Global Symbols

构建运行。你仍然什么东西看没看到,但这仍然是件好事,这足以说明工程成功的构建的并且没有警告和错误。

要构建的话,选择构建目标为 iOS Device 并按下 cmd+B 来执行构建。一旦完成,项目导航器的 Products 组别里的 libRWUIControls.a 会从红色变为黑色,这表示文件已生成。右键 libRWUIControls.a 并且选择 Show in Finder

ios_framework_successful_first_build

在这个目录中你能看到生成的静态库,libRWUIControls.a,并且公有头文件单独放在 include/RWUIControls

创建一个依赖开发项目

当你不能亲眼看到你在做什么的时候,为 iOS 开发一个 UI 控件库极其的困难,现在似乎就是这样。

没人要你盲目的工作,因此在这个部分你将会创建一个新的 Xcode 工程,它会用到你刚创建的库。这能让你通过一个示例 app 来开发 framework。自然地,这个 app 的代码会完全的与库本身的代码分离开来,这样一来会让结构更清晰。

关闭静态库工程。然后创建一个新的工程。选择 iOS/Application/Single View Application,并取名为 UIControlDevApp。设置类前缀为 RW 并指定仅 iPhone 可用。最后保存到 RWUIControls 相同的目录。

RWUIControls.xcodeproj 拖到 UIControlDevApp 组别来把 RWUIControls 作为一个依赖项。

ios_framework_import_library_into_dev_app

注意:你不能在两个不同的窗口中打开同一个工程。如果你发现你不能切换到库工程,请检查你没有在另一个 Xcode 窗口中打开它。

你可以简单的拷贝代码而不是重新创建上一篇教程的 app。首先选择 Main.storyboardRWViewController.hRWViewController.m 然后删除它们。接着拷贝 DevApp 文件夹到 UIControlDevApp 组别。

ios_framework_adding_files_to_dev_app

现在添加静态库作为示例 app 的依赖构建: * 在工程中选择 UIControlDevApp 工程。 * 导航至 UIControlDevApp 目标的 Build Phases 标签。 * 打开 Target Dependencies 面板并单击 + 来显示选择器。 * 找到 RWUIControls 静态库,单击 Add 来添加。这个动作表示当构建示例 app 的时候,Xcode 会检查是否静态库需要重新构建。

为了链接静态库,展开 Link Binary With Libraries 面板并再次点击 +。选择 libRWUIControls.a 单击添加。

这个行为会让 Xcode 把示例 app 与静态库链接起来,就像链接系统 framework 一样比如 UIKit

ios_framework_add_dependencies_to_dev_app

构建运行。你会看到跟上一篇教程中熟悉的画面。

ios_framework_dev_app_buildrun1

嵌套工程的好处就是你能够在不离开示例 app 工程的情况下继续开发静态库,正如你在不同的部位维护代码一样。你每次构建项目的时候,你也要同时检查 public/project 头成员是否正确设置。如果丢失了任何必须的头文件那么示例 app 将不会成功构建。

接下来的部分:
1. How to create a Framework for iOS (二)创建 Framework
2. How to create a Framework for iOS (三)使用 Framework 与 Bundles