iOS 静态库,动态库与 Framework 浅析

静态库与动态库的区别

首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就能够供别人使用。html

何时咱们会用到库呢?一种状况是某些代码须要给别人使用,可是咱们不但愿别人看到源码,就须要以库的形式进行封装,只暴露出头文件。另一种状况是,对于某些不会进行大的改动的代码,咱们想减小编译的时间,就能够把它打包成库,由于库是已经编译好的二进制了,编译的时候只须要 Link 一下,不会浪费编译时间。react

上面提到库在使用的时候须要 Link,Link 的方式有两种,静态和动态,因而便产生了静态库和动态库。linux

静态库

静态库即静态连接库(Windows 下的 .lib,Linux 和 Mac 下的 .a)。之因此叫作静态,是由于静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里就不会再改变了。ios

静态库的好处很明显,编译完成以后,库文件实际上就没有做用了。目标程序没有外部依赖,直接就能够运行。固然其缺点也很明显,就是会使用目标程序的体积增大。git

动态库

动态库即动态连接库(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib/.tbd)。与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。github

动态库的优势是,不须要拷贝到目标程序中,不会影响目标程序的体积,并且同一份库能够被多个程序使用(由于这个缘由,动态库也被称做共享库)。同时,编译时才载入的特性,也可让咱们随时对库进行替换,而不须要从新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。若是环境缺乏动态库或者库的版本不正确,就会致使程序没法运行(Linux 下喜闻乐见的 lib not found 错误)。编程

iOS Framework

除了上面提到的 .a 和 .dylib/.tbd 以外,Mac OS/iOS 平台还可使用 Framework。Framework 其实是一种打包方式,将库的二进制文件,头文件和有关的资源文件打包到一块儿,方便管理和分发。json

在 iOS 8 以前,iOS 平台不支持使用动态 Framework,开发者可使用的 Framework 只有苹果自家的 UIKit.Framework,Foundation.Framework 等。这种限制多是出于安全的考虑(见这里的讨论)。换一个角度讲,由于 iOS 应用都是运行在沙盒当中,不一样的程序之间不能共享代码,同时动态下载代码又是被苹果明令禁止的,没办法发挥出动态库的优点,实际上动态库也就没有存在的必要了。bootstrap

因为上面提到的限制,开发者想要在 iOS 平台共享代码,惟一的选择就是打包成静态库 .a 文件,同时附上头文件(例如微信的SDK)。可是这样的打包方式不够方便,使用时也比较麻烦,你们仍是但愿共享代码都能能像 Framework 同样,直接扔到工程里就能够用。因而人们想出了各类奇技淫巧去让 Xcode Build 出 iOS 可使用的 Framework,具体作法参考这里这里,这种方法产生的 Framework 还有 “伪”(Fake) Framework 和 “真”(Real) Framework 的区别。swift

iOS 8/Xcode 6 推出以后,iOS 平台添加了动态库的支持,同时 Xcode 6 也原生自带了 Framework 支持(动态和静态均可以),上面提到的的奇技淫巧也就没有必要了(新的作法参考这里)。为何 iOS 8 要添加动态库的支持?惟一的理由大概就是 Extension 的出现。Extension 和 App 是两个分开的可执行文件,同时须要共享代码,这种状况下动态库的支持就是必不可少的了。可是这种动态 Framework 和系统的 UIKit.Framework 仍是有很大区别。系统的 Framework 不须要拷贝到目标程序中,咱们本身作出来的 Framework 哪怕是动态的,最后也仍是要拷贝到 App 中(App 和 Extension 的 Bundle 是共享的),所以苹果又把这种 Framework 称为 Embedded Framework

Swift 支持

跟着 iOS8 / Xcode 6 同时发布的还有 Swift。若是要在项目中使用外部的代码,可选的方式只有两种,一种是把代码拷贝到工程中,另外一种是用动态 Framework。使用静态库是不支持的。

形成这个问题的缘由主要是 Swift 的运行库没有被包含在 iOS 系统中,而是会打包进 App 中(这也是形成 Swift App 体积大的缘由),静态库会致使最终的目标程序中包含重复的运行库(这是苹果自家的解释)。同时拷贝 Runtime 这种作法也会致使在纯 ObjC 的项目中使用 Swift 库出现问题。苹果声称等到 Swift 的 Runtime 稳定以后会被加入到系统当中,到时候这个限制就会被去除了(参考这个问题 的问题描述,也是来自苹果自家文档)。

CocoaPods 的作法

在纯 ObjC 的项目中,CocoaPods 使用编译静态库 .a 方法将代码集成到项目中。在 Pods 项目中的每一个 target 都对应这一个 Pod 的静态库。不过在编译过程当中并不会真的产出 .a 文件。若是须要 .a 文件的话,能够参考这里,或者使用 CocoasPods-Packager这个插件。

当不想发布代码的时候,也可使用 Framework 发布 Pod,CocoaPods 提供了vendored_framework 选项来使用第三方 Framework,具体的作法能够参考这里这里

对于 Swift 项目,CocoaPods 提供了动态 Framework 的支持,经过 use_frameworks!选项控制。

更多有关代码分发的扩展资料能够参考这篇博客: http://geeklu.com/2014/02/objc-lib/

参考资料

 

 

 

 

 

使用静态库的好处

1,模块化,分工合做

2,避免少许改动常常致使大量的重复编译链接

3,也能够重用,注意不是共享使用

动态库使用有以下好处:

1使用动态库,能够将最终可执行文件体积缩小

2使用动态库,多个应用程序共享内存中得同一份库文件,节省资源

3使用动态库,能够不从新编译链接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。

从1能够得出,将整个应用程序分模块,团队合做,进行分工,影响比较小。

等其余好处,

从2能够看出,其实动态库应该叫共享库,那么从这个意义上来讲,苹果禁止iOS开发中使用动态库就能够理解了:

由于在如今的iPhone,iPodTouch,iPad上面程序都是单进程的,也就是某一时刻只有一个进程在运行,那么你写个共享库,

----共享给谁?(你使用的时候只有你一个应用程序存在,其余的应该被挂起了,即使是能够同时多个进程运行,别人能使用你的共享库里的东西吗?你这个是给你本身的程序定制的。)

----目前苹果的AppStore不支持模块更新,没法更新某个单独文件(除非本身写一个更新机制:有本身的服务端放置最新动态库文件)

至于苹果为啥禁止ios开发使用动态库我就猜到上面俩缘由

深刻理解iPhone静态库

在实际的编程过程当中,一般会把一些公用函数制成函数库,供其它程序使用,一则提搞了代码的复用;二则提搞了核心技术的保密程度。因此在实际的项目开发中,常常会使用到函数库,函数库分为静态库和动态库两种。和多数人所熟悉的动态语言和静态语言同样,这里的所谓静态和动态是相对编译期和运行期的:静态库在程序编译时会被连接到目标代码中,程序运行时将再也不须要改静态库;而动态库在程序编译时并不会被连接到目标代码中,只是在程序运行时才被载入,由于在程序运行期间还须要动态库的存在。

iPhone官方只支持静态库联编。

深刻理解framework(框架,其实至关于静态框架,不是动态库)

打包framework仍是一个比较重要的功能,能够用来作一下事情:

(1)封装功能模块,好比有比较成熟的功能模块封装成一个包,而后之后本身或其余同事用起来比较方便。

(2)封装项目,有时候会遇到这个状况,就是一家公司找了两个开发公司作两个项目,而后要求他们的项目中的一个嵌套进另外一个项目,此时也能够把呗嵌套的项目打包成framework放进去,这样比较方便。

咱们为何须要框架(Framework)?

要想用一种开发者友好的方式共享库是很麻烦的。你不只仅须要包含库自己,还要加入全部的头文件,资源等等。

苹果解决这个问题的方式是框架(framework)。基本上,这是含有固定结构并包含了引用该库时所必需的全部东西的文件夹。不幸的是,iOS禁止全部的动态库。同时,苹果也从Xcode中移除了建立静态iOS框架的功能。

Xcode仍然能够支持建立框架的功能,重启这个功能,咱们须要对Xcode作一些小小的改动。

把代码封装在静态框架是被app store所容许的。尽管形式不一样,本质上它仍然是一种静态库。

框架(Framework)的类别

大部分框架都是动态连接库的形式。由于只有苹果才能在iOS设备上安装动态库,因此咱们没法建立这种类型的框架。

静态连接库和动态库同样,只不过它是在编译时连接二进制代码,所以使用静态库不会有动态库那样的问题(即除了苹果谁也不能在iOS上使用动态库)。

“伪”框架是经过破解Xcode的目标Bundle(使用某些脚本)来实现的。它在表面上以及使用时跟静态框架并没有区别。“伪”框架项目的功能几乎和真实的框架项目没有区别(不是所有)。

“嵌入”框架是静态框架的一个包装,以便Xcode能获取框架内的资源(图片、plist、nib等)。

本次发布包括了建立静态框架和“伪”框架的模板,以及两者的“嵌入”框架。

用哪种模板?

本次发布有两个模板,每一个模板都有“强”“弱”两个类别。你能够选择最适合一种(或者两种都安装上)。

最大的不一样是Xcode不能建立“真”框架,除非你安装静态框架文件xcspec在Xcode中。这真是一个遗憾(这个文件是给项目使用的,而不是框架要用的)。

简单第

简单说,你能够这样决定用哪种模板:

若是你不想修改Xcode,那么请使用“伪”框架版本

若是你只是想共享二进制(不是项目),两种均可以

若是你想把框架共享给不想修改Xcode的开发者,使用“伪”框架版本

若是你想把框架共享给修改过Xcode的开发者,使用“真”框架版本

若是你想把框架项目做为另外一个项目的依赖(经过workspace或者子项目的方式),请使用“真”框架(或者“伪”框架,使用-framework——见后)

若是你想在你的框架项目中加入其余静态库/框架,并把它们也连接到最终结果以便不须要单独添加到用户项目中,使用“伪”框架

“伪”框架

“伪”框架是破解的“reloacatable object file”(可重定位格式的目标文件, 保存着代码和数据,适合于和其余的目标文件链接到一块儿,用来建立一个可执行目标文件或者是一个可共享目标文件),它可让Xcode编译出相似框架的东西——其实也是一个bundle。

“伪框架”模板把整个过程分为几个步骤,用某些脚本去产生一个真正的静态框架(基于静态库而不是reloacatable object file)。并且,框架项目仍是把它定义为wrapper.cfbundle类型,一种Xcode中的“二等公民”。

所以它跟“真”静态框架同样能够正常工做,但当存在依赖关系时就有麻烦了。

依赖问题

若是不使用依赖,只是建立普通的项目是没有任何问题的。可是若是使用了项目依赖(好比在workspace中),Xcode就悲剧了。当你点击“Link Binary With Libraries”下方的’+’按钮时,“伪框架”没法显示在列表中。你能够从你的“伪”框架项目的Products下面将它手动拖入,但当你编辑你的主项目时,会出现警告:

warning: skipping file '/somewhere/MyFramework.framework' (unexpectedfile type 'wrapper.cfbundle' in Frameworks & Libraries build phase)

并伴随“伪”框架中的连接错误。

幸运的是,有个办法来解决它。你能够在”Other Linker Flags”中用”-framwork”开关手动告诉linker去使用你的框架进行连接:

-framework MyFramework

警告仍然存在,但起码能正确连接了。

添加其余的库/框架

若是你加入其余静态(不是动态)库/框架到你的“伪”框架项目中,它们将“连接”进你最终的二进制框架文件中。在“真”框架项目中,它们是纯引用,而不是连接。

你能够在项目中仅仅包含头文件而不是静态库/框架自己的方式避免这种状况(以便编译经过)。

“真”框架

“真”框架各个方面都符合“真”的标准。它是真正的静态框架,正如使用苹果在从Xcode中去除的那个功能所建立的同样。

为了能建立真正的静态框架项目,你必需在Xcode中安装一个xcspec文件。

若是你发布一个“真”框架项目(而不是编译),但愿去编译这个框架的人必需也安装xcspec文件(使用本次发布的安装脚本),以便Xcode能理解目标类型。

注意:若是你正在发布彻底编译的框架,而不是框架项目,最终用户并不须要安装任何东西。

我已经提交一个报告给苹果,但愿他们在Xcode中更新这个文件,但那须要一点时间.OpenRadarlink here

加其余静态库/框架

若是你加入其余静态(不是动态)库/框架到你的“真”框架项目,它们只会被引用,而不会象“伪”框架同样被连接到最终的二进制文件中。

从早期版本升级

若是你是从Mk6或者更早的版本升级,同时使用“真”静态框架,而且使用Xcode4.2.1之前的版本,请运行uninstall_legacy.sh以卸载早期用于Xcode的全部修正。而后再运行install.sh,重启Xcode。若是你使用Xcode4.3之后,只须要运行install.sh并重启Xcode。

安装

分别运行Real Framework目录或Fake Framework目录下的install.sh脚本进行安装(或者两个你都运行)。

重启Xcode,你将在新项目向导的Framework&Library下看到StaticiOS Framework(或者Fake Static iOS Framework)。

卸载请运行unistall.sh脚本并重启Xcode。

建立一个iOS框架项目

建立新项目。

项目类型选择Framework&Library下的Static iOS Framework(或者Fake Static iOS Framework)。

选择“包含单元测试”(可选的)。

在target中加入类、资源等。

凡是其余项目要使用的头文件,必需声明为public。进入target的Build Phases页,展开Copy Headers项,把须要public的头文件从Project或Private部分拖拽到Public部分。

编译你的 iOS 框架

选择指定target的scheme

修改scheme的Run配置(可选)。Run配置默认使用Debug,但在准备部署的时候你可能想使用Release。

编译框架(不管目标为iOS device和Simulator都会编译出相同的二进制,所以选谁都无所谓了)。

从Products下选中你的framework,“show in Finder”。

在build目录下有两个文件夹:(yourframework).frameworkand(your framework).embeddedframework.

若是你的框架只有代码,没有资源(好比图片、脚本、xib、coredata的momd文件等),你能够把(yourframework).framework分发给你的用户就好了。若是还包含有资源,你必需分发(your framework).embeddedframework给你的用户。

为何须要embedded framework?由于Xcode不会查找静态框架中的资源,若是你分发(your framework).framework, 则框架中的全部资源都不会显示,也不可用。

一个embedded framework只是一个framework以外的附加的包,包括了这个框架的全部资源的符号连接。这样作的目的是让Xcode可以找到这些资源。

使用iOS 框架

iOS框架和常规的Mac OS动态框架差很少,只是它是静态连接的而已。

在你的项目中使用一个框架,只需把它拖仅你的项目中。在包含头文件时,记住使用尖括号而不是双引号括住框架名称。例如,对于框架MyFramework:

#import

使用问题

Headers Not Found

若是Xcode找不到框架的头文件,你多是忘记将它们声明为public了。参考“建立一个iOS框架项目”第5步。

No Such Product Type

若是你没有安装iOS Universal Framework在Xcode,并企图编译一个universal框架项目(对于“真”框架,不是“假”框架),这会致使下列错误:

target specifies product type 'com.apple.product-type.framework.static',but there's no such product type for the 'iphonesimulator' platform

为了编译“真”iOS静态框架,Xcode须要作一些改动,所以为了编译“真”静态框架项目,请在全部的开发环境中安装它(对于使用框架的用户不须要,只有要编译框架才须要)。

The selected run destination is not valid for this action

有时,Xcode出错并加载了错误的active设置。首先,请尝试重启Xcode。若是错误继续存在,Xcode产生了一个坏的项目(由于Xcode4的一个bug,任何类型的项目都会出现这个问题)。若是是这样,你须要建立一个新项目重来一遍。

连接警告

第一次编译框架target时,Xcdoe会在连接阶段报告找不到文件夹:

ld: warning: directory not found for option'-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos'

此时,能够clean并从新编译target,警告会消除。

Core Data momd not found

对于框架项目和应用程序项目,Xcode会以不一样的方式编译momd(托管对象模型文件)。Xcode会简单地在根目录建立.mom文件,而不会建立一个.momd目录(目录中包含VersionInfo.plist和.mom文件)。

这意味着,当从一个embedded framework的model中实例化NSManagedObjectModel时,你必需使用.mom扩展名做为model的URL,而不是采用.momd扩展名。

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"MyModel" withExtension:@"mom"];

Unknown class MyClass in Interface Builder file.

因为静态框架采用静态连接,linker会剔除全部它认为无用的代码。不幸的是,linker不会检查xib文件,所以若是类是在xib中引用,而没有在O-C代码中引用,linker将从最终的可执行文件中删除类。这是linker的问题,不是框架的问题(当你编译一个静态库时也会发生这个问题)。苹果内置框架不会发生这个问题,由于他们是运行时动态加载的,存在于iOS设备固件中的动态库是不可能被删除的。

有两个解决的办法:

让框架的最终用户关闭linker的优化选项,经过在他们的项目的Other Linker Flags中添加-ObjC和-all_load。

在框架的另外一个类中加一个该类的代码引用。例如,假设你有个MyTextField类,被linker剔除了。假设你还有一个MyViewController,它在xib中使用了MyTextField,MyViewController并无被剔除。你应该这样作:

在MyTextField中:

+ (void)forceLinkerLoad_ {}

在MyViewController中:

+(void) initialize {     [MyTextField forceLinkerLoad_]; }

他们仍然须要添加-ObjC到linker设置,但不须要强制all_load了。

第2种方法须要你多作一点工做,但却让最终用户避免在使用你的框架时关闭linker优化(关闭linker优化会致使object文件膨胀)。

unexpected file type 'wrapper.cfbundle' in Frameworks &Libraries build phase

这个问题发生在把“假”框架项目做为workspace的依赖,或者把它看成子项目时(“真”框架项目没有这个问题)。尽管这种框架项目产生了正确的静态框架,但Xcode只能从项目文件中看出这是一个bundle,所以它在检查依赖性时发出一个警告,并在linker阶段跳过它。

你能够手动添加一个命令让linker在连接阶段能正确连接。在依赖你的静态框架的项目的OtherLinker Flags中加入:

-framework MyFramework

警告仍然存在, 但不会致使连接失败。

Libraries being linked or not being linked into the finalframework

很不幸, “真”框架和“假”框架模板在处理引入的静态库/框架的工做方式不一样的。

“真”框架模板采用正常的静态库生成步骤,不会连接其余静态库/框架到最终生产物中。

“假”框架模板采用“欺骗”Xcode的手段,让它认为是在编译一个可重定位格式的目标文件,在连接阶段就如同编译一个可执行文件,把全部的静态代码文件连接到最终生成物中(尽管不会检查是否确实目标代码)。为了实现象“真”框架同样的效果,你能够只包含库/框架的头文件到你的项目中,而不须要包含库/框架自己。

Unrecognized selector in (some class with a category method)

若是你的静态库或静态框架包含了一个模块(只在类别代码中声明,没有类实现),linker会搞不清楚,并把代码从二进制文件中剔除。由于在最终生成的文件中没有这个方法,因此当调用这个类别中定义的方法时,会报一个“unrecognizedselector”异常。

要解决这个,在包含这个类别的模块代码中加一个“假的”类。linker发现存在完整的O-C类,会将类别代码连接到模块。

我写了一个头文件 LoadableCategory.h,以减轻这个工做量:

#import "SomeConcreteClass+MyAdditions.h"

#import "LoadableCategory.h"  MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions);   @implementation SomeConcreteClass(MyAdditions)

...

@end

在使用这个框架时,仍然还须要在Build Setting的Other Linker Flags中加入-ObjC。

执行任何代码前单元测试崩溃

若是你在Xcode4.3中建立静态框架(或库)target时,勾选了“withunit tests”,当你试图运行单元测试时,它会崩溃:

Thread 1: EXC_BAD_ACCESS (code=2, address=0x0) 0 0x00000000 --- 15 dyldbootstrap:start(...)

这是lldb中的一个bug。你能够用GDB来运行单元测试。编辑scheme,选择Test,在Info标签中将调试器Debugger从LLDB改成GDB。

 

原文连接:http://www.jianshu.com/p/4666ce7dc622
 
 
 
 
 
 
 

前言

1.静态库和动态库有什么异同?

静态库:连接时完整地拷贝至可执行文件中,被屡次使用就有多份冗余拷贝。利用静态函数库编译成的文件比较大,由于整个 函数库的全部数据都会被整合进目标代码中,他的优势就显而易见了,即编译后的执行程序不须要外部的函数库支持,由于全部使用的函数都已经被编译进去了。固然这也会成为他的缺点,由于若是静态函数库改变了,那么你的程序必须从新编译。

动态库:连接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。因为函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,因此程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,因此动态函数库的升级比较方便。

静态库和动态库都是闭源库,只能拿来知足某个功能的使用,不会暴露内部具体的代码信息,而从github上下载的第三方库大可能是开源库

静态库和动态库都是由*.o目标文件生成

使用静态库的好处

  • 模块化,分工合做
  • 避免少许改动常常致使大量的重复编译链接
  • 也能够重用,注意不是共享使用

动态库使用有以下好处:

  • 能够将最终可执行文件体积缩小
  • 多个应用程序共享内存中得同一份库文件,节省资源
  • 能够不从新编译链接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。

将整个应用程序分模块,团队合做,进行分工,影响比较小。

其实动态库应该叫共享库,那么从这个意义上来讲,苹果禁止iOS开发中使用动态库就能够理解了: 由于在如今的iPhone,iPodTouch,iPad上面程序都是单进程的,也就是某一时刻只有一个进程在运行,那么你写个共享库

----共享给谁?(你使用的时候只有你一个应用程序存在,其余的应该被挂起了,即使是能够同时多个进程运行,别人能使用你的共享库里的东西吗?你这个是给你本身的程序定制的。)
    ----目前苹果的AppStore不支持模块更新,没法更新某个单独文件(除非本身写一个更新机制:有本身的服务端放置最新动态库文件)

至于苹果为啥禁止ios开发使用动态库我就猜到上面俩缘由

2.这两种库都有哪些文件格式?

静态库:.a和.framework (windows:.lib , linux: .a)

动态库:.dylib和.framework(系统提供给咱们的framework都是动态库!)(windows:.dll , linux: .so)

注意:二者都有framework的格式,可是当你建立一个framework文件时,系统默认是动态库的格式,若是想作成静态库,须要在buildSetting中将Mach-O Type选项设置为Static Library就好了!

3..a文件和.framework文件的区别?

.a是一个纯二进制文件,不能直接拿来使用,须要配合头文件、资源文件一块儿使用。

将静态库打包的时候,只能打包代码资源,图片、本地json文件和xib等资源文件没法打包进去,使用.a静态库的时候须要三个组成部分:.a文件+须要暴露的头文件+资源文件;

.framework中除了有二进制文件以外还有资源文件,能够拿来直接使用。

4.制做静态库须要注意的几点:

  • 注意理解:不管是.a静态库还.framework静态库,咱们须要的都是二进制文件+.h+其它资源文件的形式,不一样的是,.a自己就是二进制文件,须要咱们本身配上.h和其它文件才能使用,而.framework自己已经包含了.h和其它文件,能够直接使用。
  • 图片资源的处理:两种静态库,通常都是把图片文件单独的放在一个.bundle文件中,通常.bundle的名字和.a或.framework的名字相同。.bundle文件很好弄,新建一个文件夹,把它更名为.bundle就能够了,右键,显示包内容能够向其中添加图片资源。
  • category是咱们实际开发项目中常常用到的,把category打成静态库是没有问题的,可是在用这个静态库的工程中,调用category中的方法时会有找不到该方法的运行时错误(selector not recognized),解决办法是:在使用静态库的工程中配置other linkerflags的值为-ObjC。
  • 若是一个静态库很复杂,须要暴露的.h比较多的话,就能够在静态库的内部建立一个.h文件(通常这个.h文件的名字和静态库的名字相同),而后把全部须要暴露出来的.h文件都集中放在这个.h文件中,而那些本来须要暴露的.h都不须要再暴露了,只须要把.h暴露出来就能够了。

5.framework动态库的主要做用:

framework原本是苹果专属的内部提供的动态库文件格式,可是自从2014年WWDC以后,开发者也能够自定义建立framework实现动态更新(绕过apple store审核,从服务器发布更新版本)的功能,这与苹果限定的上架的app必须通过apple store的审核制度是冲突的,因此含有自定义的framework的app是没法在商店上架的,可是若是开发的是企业内部应用,就能够考虑尝试使用动态更新技术来将多个独立的app或者功能模块集成在一个app上面!(笔者开发的就是企业内部使用的app,咱们将企业官网中的板块开发成4个独立的app,而后将其改造为framework文件集成在一款平台级的app当中进行使用)

目前 iOS 上的动态更新方案主要有如下 4 种:

  • HTML 5
  • lua(wax)hotpatch
  • react native
  • framework

前面三种都是经过在应用内搭建一个运行环境来实现动态更新(HTML 5 是原生支持),在用户体验、与系统交互上有必定的限制,对开发者的要求也更高(至少得熟悉 lua 或者 js)。

使用 framework 的方式来更新能够不依赖第三方库,使用原生的 OC/Swift 来开发,体验更好,开发成本也更低。

因为 Apple 不但愿开发者绕过 App Store 来更新 app,所以只有对于不须要上架的应用,才能以 framework 的方式实现 app 的更新。

主要思路

将 app 中的某个模块(好比一个 tab)的内容独立成一个 framework 的形式动态加载,在 app 的 main bundle 中,当 app 启动时从服务器上下载新版本的 framework 并加载便可达到动态更新的目的。

实战

建立一个普通工程 DynamicUpdateDemo,其包含一个 framework 子工程 Module。也能够将 Module 建立为独立的工程,建立工程的过程再也不赘述。

依赖

在主工程的 Build Phases > Target Dependencies 中添加 Module,而且添加一个 New Copy Files Phase。

这样,打包时会将生成的 Module.framework 添加到 main bundle 的根目录下。

加载

主要的代码以下:

- (UIViewController *)loadFrameworkNamed:(NSString *)bundleName {
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDirectory = nil;
    if ([paths count] != 0) {
        documentDirectory = [paths objectAtIndex:0];
    }

    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *bundlePath = [documentDirectory stringByAppendingPathComponent:[bundleName stringByAppendingString:@".framework"]];

    // Check if new bundle exists
    if (![manager fileExistsAtPath:bundlePath]) {
        NSLog(@"No framework update");
        bundlePath = [[NSBundle mainBundle]
                      pathForResource:bundleName ofType:@"framework"];

        // Check if default bundle exists
        if (![manager fileExistsAtPath:bundlePath]) {
            UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Oooops" message:@"Framework not found" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
            [alertView show];
            return nil;
        }
    }

    // Load bundle
    NSError *error = nil;
    NSBundle *frameworkBundle = [NSBundle bundleWithPath:bundlePath];
    if (frameworkBundle && [frameworkBundle loadAndReturnError:&error]) {
        NSLog(@"Load framework successfully");
    }else {
        NSLog(@"Failed to load framework with err: %@",error);
        return nil;
    }

    // Load class
    Class PublicAPIClass = NSClassFromString(@"PublicAPI");
    if (!PublicAPIClass) {
        NSLog(@"Unable to load class");
        return nil;
    }

    NSObject *publicAPIObject = [PublicAPIClass new];
    return [publicAPIObject performSelector:@selector(mainViewController)];
}

代码先尝试在 Document 目录下寻找更新后的 framework,若是没有找到,再在 main bundle 中寻找默认的 framework。 其中的关键是利用 OC 的动态特性 NSClassFromString 和 performSelector 加载 framework 的类而且执行其方法。

framework 和 host 工程资源共用

第方三库

Class XXX is implemented in both XXX and XXX. One of the two will be used. Which one is undefined.

这是当 framework 工程和 host 工程连接了相同的第三方库或者类形成的。

为了让打出的 framework 中不包含 host 工程中已包含的三方库(如 cocoapods 工程编译出的 .a 文件),能够这样:

  • 删除 Build Phases > Link Binary With Libraries 中的内容(若有)。此时编译会提示三方库中包含的符号找不到。

  • 在 framework 的 Build Settings > Other Linker Flags 添加 -undefined dynamic_lookup。必须保证 host 工程编译出的二进制文件中包含这些符号。

类文件

尝试过在 framework 中引用 host 工程中已有的文件,经过 Build Settings > Header Search Paths 中添加相应的目录,Xcode 在编译的时候能够成功(由于添加了 -undefined dynamic_lookup),而且 Debug 版本是能够正常运行的,可是 Release 版本动态加载时会提示找不到符号:

Error Domain=NSCocoaErrorDomain Code=3588 "The bundle “YourFramework” couldn’t be loaded." (dlopen(/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, 265): Symbol not found: _OBJC_CLASS_$_BorderedView
      Referenced from: /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework
      Expected in: flat namespace
     in /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework) UserInfo=0x174276900 {NSLocalizedFailureReason=The bundle couldn’t be loaded., NSLocalizedRecoverySuggestion=Try reinstalling the bundle., NSFilePath=/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, NSDebugDescription=dlopen(/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, 265): Symbol not found: _OBJC_CLASS_$_BorderedView
      Referenced from: /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework
      Expected in: flat namespace
     in /var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework/YourFramework, NSBundlePath=/var/mobile/Containers/Bundle/Application/5691FB75-408A-4D9A-9347-BC7B90D343C1/YourApp.app/YourFramework.framework, NSLocalizedDescription=The bundle “YourFramework” couldn’t be loaded.}

由于 Debug 版本暴露了全部自定义类的符号以便于调试,所以你的 framework 能够找到相应的符号,而 Release 版本则不会。

目前能想到的方法只有将相同的文件拷贝一份到 framework 工程里,而且更改类名。

访问 framework 中的图片

在 storyboard/xib 中能够直接访问图片,代码中访问的方法以下:

UIImage *image = [UIImage imageNamed:@"YourFramework.framework/imageName"]

注意:使用代码方式访问的图片不能够放在 xcassets 中,不然获得的将是 nil。而且文件名必须以 @2x/@3x 结尾,大小写敏感。由于 imageNamed: 默认在 main bundle 中查找图片。

常见错误

Architecture

dlopen(/path/to/framework, 9): no suitable image found.  Did find:
/path/to/framework: mach-o, but wrong architecture

这是说 framework 不支持当前机器的架构。 经过

lipo -info /path/to/MyFramework.framework/MyFramework

能够查看 framework 支持的 CPU 架构。

碰到这种错误,通常是由于编译 framework 的时候,scheme 选择的是模拟器,应该选择iOS Device。

此外,若是没有选择iOS Device,编译完成后,Products 目录下的 .framework 文件名会一直是红色,只有在 Derived Data 目录下才能找到编译生成的 .framework 文件。

关于other linker flag

使用静态库或者动态库的时候极易发生连接错误,并且大多发生在加载framework中category的状况!根本缘由在于Objective-C的连接器并不会为每一个方法创建符号表,而是仅仅为类创建了符号表。这样的话,若是静态库中定义了已存在的一个类的分类,连接器就会觉得这个类已经存在,不会把分类和核心类的代码合起来。这样的话,在最后的可执行文件中,就会缺乏分类里的代码,这样函数调用就失败了。常见的设置方法就是在other linker flag中添加一个语句:-all_load,可是这样也并非万能的,具体解析请参考连接:http://my.oschina.net/u/728866/blog/194741

注意:当flag里面添加了注释却仍是没法使用的时候,可能报flag与bitcode冲突的问题尤为是第三方库可能和bitcode冲突),这样的话就须要将bitcode设置为NO!

bitcode的具体做用不作详谈,可参考:http://www.jianshu.com/p/3e1b4e2d06c6

签名

系统在加载动态库时,会检查 framework 的签名,签名中必须包含 TeamIdentifier 而且 framework 和 host app 的 TeamIdentifier 必须一致。

若是不一致,不然会报下面的错误:

Error loading /path/to/framework: dlopen(/path/to/framework, 265): no suitable image found. Did find:
/path/to/framework: mmap() error 1

此外,若是用来打包的证书是 iOS 8 发布以前生成的,则打出的包验证的时候会没有 TeamIdentifier 这一项。这时在加载 framework 的时候会报下面的错误:

[deny-mmap] mapped file has no team identifier and is not a platform binary:
/private/var/mobile/Containers/Bundle/Application/5D8FB2F7-1083-4564-94B2-0CB7DC75C9D1/YourAppNameHere.app/Frameworks/YourFramework.framework/YourFramework

能够经过 codesign 命令来验证。

codesign -dv /path/to/YourApp.app

若是证书太旧,输出的结果以下:

Executable=/path/to/YourApp.app/YourApp
Identifier=com.company.yourapp
Format=bundle with Mach-O thin (armv7)
CodeDirectory v=20100 size=221748 flags=0x0(none) hashes=11079+5 location=embedded
Signature size=4321
Signed Time=2015年10月21日 上午10:18:37
Info.plist entries=42
TeamIdentifier=not set
Sealed Resources version=2 rules=12 files=2451
Internal requirements count=1 size=188

注意其中的 TeamIdentifier=not set。

采用 swift 加载 libswiftCore.dylib 这个动态库的时候也会遇到这个问题,对此Apple 官方的解释是:

To correct this problem, you will need to sign your app using code signing certificates with the Subject Organizational Unit (OU) set to your Team ID. All Enterprise and standard iOS developer certificates that are created after iOS 8 was released have the new Team ID field in the proper place to allow Swift language apps to run.

If you are an in-house Enterprise developer you will need to be careful that you do not revoke a distribution certificate that was used to sign an app any one of your Enterprise employees is still using as any apps that were signed with that enterprise distribution certificate will stop working immediately.

只能经过从新生成证书来解决这个问题。可是 revoke 旧的证书会使全部用户已经安装的,用该证书打包的 app 没法运行。

等等,咱们就跪在这里了吗?!

如今企业证书的有效期是三年,当证书过时时,其打包的应用就不能运行,那企业应用怎么来更替证书呢?

Apple 为每一个帐号提供了两个证书,这两个证书能够同时生效,这样在正在使用的证书过时以前,可使用另一个证书打包发布,让用户升级到新版本。

也就是说,可使用另一个证书来打包应用,而且能够覆盖安装使用旧证书打包的应用。详情能够看 Apple 文档

深刻理解iPhone静态库

在实际的编程过程当中,一般会把一些公用函数制成函数库,供其它程序使用,一则提搞了代码的复用;二则提搞了核心技术的保密程度。因此在实际的项目开发中,常常会使用到函数库,函数库分为静态库和动态库两种。和多数人所熟悉的动态语言和静态语言同样,这里的所谓静态和动态是相对编译期和运行期的:静态库在程序编译时会被连接到目标代码中,程序运行时将再也不须要改静态库;而动态库在程序编译时并不会被连接到目标代码中,只是在程序运行时才被载入,由于在程序运行期间还须要动态库的存在。

深刻理解framework(框架,至关于静态框架,不是动态库)

打包framework仍是一个比较重要的功能,能够用来作一下事情:

  • 封装功能模块,好比有比较成熟的功能模块封装成一个包,而后之后本身或其余同事用起来比较方便。
  • 封装项目,有时候会遇到这个状况,就是一家公司找了两个开发公司作两个项目,而后要求他们的项目中的一个嵌套进另外一个项目,此时也能够把呗嵌套的项目打包成framework放进去,这样比较方便。

咱们为何须要框架(Framework)?

要想用一种开发者友好的方式共享库是很麻烦的。你不只仅须要包含库自己,还要加入全部的头文件,资源等等。

苹果解决这个问题的方式是框架(framework)。基本上,这是含有固定结构并包含了引用该库时所必需的全部东西的文件夹。不幸的是,iOS禁止全部的动态库。同时,苹果也从Xcode中移除了建立静态iOS框架的功能。

Xcode仍然能够支持建立框架的功能,重启这个功能,咱们须要对Xcode作一些小小的改动。

把代码封装在静态框架是被app store所容许的。尽管形式不一样,本质上它仍然是一种静态库。

框架(Framework)的类别

大部分框架都是动态连接库的形式。由于只有苹果才能在iOS设备上安装动态库,因此咱们没法建立这种类型的框架。

静态连接库和动态库同样,只不过它是在编译时连接二进制代码,所以使用静态库不会有动态库那样的问题(即除了苹果谁也不能在iOS上使用动态库)。

“伪”框架是经过破解Xcode的目标Bundle(使用某些脚本)来实现的。它在表面上以及使用时跟静态框架并没有区别。“伪”框架项目的功能几乎和真实的框架项目没有区别(不是所有)。

“嵌入”框架是静态框架的一个包装,以便Xcode能获取框架内的资源(图片、plist、nib等)。 本次发布包括了建立静态框架和“伪”框架的模板,以及两者的“嵌入”框架。

用哪种模板?

本次发布有两个模板,每一个模板都有“强”“弱”两个类别。你能够选择最适合一种(或者两种都安装上)。 最大的不一样是Xcode不能建立“真”框架,除非你安装静态框架文件xcspec在Xcode中。这真是一个遗憾(这个文件是给项目使用的,而不是框架要用的)。

简单说,你能够这样决定用哪种模板:

  • 若是你不想修改Xcode,那么请使用“伪”框架版本
  • 若是你只是想共享二进制(不是项目),两种均可以
  • 若是你想把框架共享给不想修改Xcode的开发者,使用“伪”框架版本
  • 若是你想把框架共享给修改过Xcode的开发者,使用“真”框架版本
  • 若是你想把框架项目做为另外一个项目的依赖(经过workspace或者子项目的方式),请使用“真”框架(或者“伪”框架,使用-framework——见后)
  • 若是你想在你的框架项目中加入其余静态库/框架,并把它们也连接到最终结果以便不须要单独添加到用户项目中,使用“伪”框架

“伪”框架

“伪”框架是破解的“reloacatable object file”(可重定位格式的目标文件, 保存着代码和数据,适合于和其余的目标文件链接到一块儿,用来建立一个可执行目标文件或者是一个可共享目标文件),它可让Xcode编译出相似框架的东西——其实也是一个bundle。

“伪框架”模板把整个过程分为几个步骤,用某些脚本去产生一个真正的静态框架(基于静态库而不是reloacatable object file)。并且,框架项目仍是把它定义为wrapper.cfbundle类型,一种Xcode中的“二等公民”。

所以它跟“真”静态框架同样能够正常工做,但当存在依赖关系时就有麻烦了。

依赖问题

若是不使用依赖,只是建立普通的项目是没有任何问题的。可是若是使用了项目依赖(好比在workspace中),Xcode就悲剧了。当你点击“Link Binary With Libraries”下方的’+’按钮时,“伪框架”没法显示在列表中。你能够从你的“伪”框架项目的Products下面将它手动拖入,但当你编辑你的主项目时,会出现警告:

warning: skipping file '/somewhere/MyFramework.framework' (unexpectedfile type 'wrapper.cfbundle' in Frameworks & Libraries build phase) 并伴随“伪”框架中的连接错误。

幸运的是,有个办法来解决它。你能够在”Other Linker Flags”中用”-framwork”开关手动告诉linker去使用你的框架进行连接:

-framework MyFramework

警告仍然存在,但起码能正确连接了。

添加其余的库/框架

若是你加入其余静态(不是动态)库/框架到你的“伪”框架项目中,它们将“连接”进你最终的二进制框架文件中。在“真”框架项目中,它们是纯引用,而不是连接。

你能够在项目中仅仅包含头文件而不是静态库/框架自己的方式避免这种状况(以便编译经过)。

“真”框架

“真”框架各个方面都符合“真”的标准。它是真正的静态框架,正如使用苹果在从Xcode中去除的那个功能所建立的同样。

为了能建立真正的静态框架项目,你必需在Xcode中安装一个xcspec文件。

若是你发布一个“真”框架项目(而不是编译),但愿去编译这个框架的人必需也安装xcspec文件(使用本次发布的安装脚本),以便Xcode能理解目标类型。

注意:若是你正在发布彻底编译的框架,而不是框架项目,最终用户并不须要安装任何东西。 我已经提交一个报告给苹果,但愿他们在Xcode中更新这个文件,但那须要一点时间。

加其余静态库/框架

若是你加入其余静态(不是动态)库/框架到你的“真”框架项目,它们只会被引用,而不会象“伪”框架同样被连接到最终的二进制文件中。

从早期版本升级

若是你是从Mk6或者更早的版本升级,同时使用“真”静态框架,而且使用Xcode4.2.1之前的版本,请运行uninstall_legacy.sh以卸载早期用于Xcode的全部修正。而后再运行install.sh,重启Xcode。若是你使用Xcode4.3之后,只须要运行install.sh并重启Xcode。

安装

分别运行Real Framework目录或Fake Framework目录下的install.sh脚本进行安装(或者两个你都运行)。

重启Xcode,你将在新项目向导的Framework&Library下看到StaticiOS Framework(或者Fake Static iOS Framework)。

卸载请运行unistall.sh脚本并重启Xcode。

建立一个iOS框架项目

  1. 建立新项目。
  2. 项目类型选择Framework&Library下的Static iOS Framework(或者Fake Static iOS Framework)。
  3. 选择“包含单元测试”(可选的)。
  4. 在target中加入类、资源等。
  5. 凡是其余项目要使用的头文件,必需声明为public。进入target的Build Phases页,展开Copy Headers项,把须要public的头文件从Project或Private部分拖拽到Public部分。

编译你的 iOS 框架

  1. 选择指定target的scheme
  2. 修改scheme的Run配置(可选)。Run配置默认使用Debug,但在准备部署的时候你可能想使用Release。
  3. 编译框架(不管目标为iOS device和Simulator都会编译出相同的二进制,所以选谁都无所谓了)。
  4. 从Products下选中你的framework,“show in Finder”。

在build目录下有两个文件夹:(yourframework).framework and (your framework).embeddedframework.

若是你的框架只有代码,没有资源(好比图片、脚本、xib、coredata的momd文件等),你能够把(yourframework).framework 分发给你的用户就好了。若是还包含有资源,你必需分发(your framework).embeddedframework给你的用户。

为何须要embedded framework?由于Xcode不会查找静态框架中的资源,若是你分发(your framework).framework, 则框架中的全部资源都不会显示,也不可用。

一个embedded framework只是一个framework以外的附加的包,包括了这个框架的全部资源的符号连接。这样作的目的是让Xcode可以找到这些资源。

使用iOS 框架

OS框架和常规的Mac OS动态框架差很少,只是它是静态连接的而已。

在你的项目中使用一个框架,只需把它拖仅你的项目中。在包含头文件时,记住使用尖括号而不是双引号括住框架名称。例如,对于框架MyFramework:

import <MyFramework MyClass.h=""></MyFramework>

使用问题

Headers Not Found

若是Xcode找不到框架的头文件,你多是忘记将它们声明为public了。参考“建立一个iOS框架项目”第5步。

No Such Product Type

若是你没有安装iOS Universal Framework在Xcode,并企图编译一个universal框架项目(对于“真”框架,不是“假”框架),这会致使下列错误:

target specifies product type 'com.apple.product-type.framework.static',but there's no such product type for the 'iphonesimulator' platform

为了编译“真”iOS静态框架,Xcode须要作一些改动,所以为了编译“真”静态框架项目,请在全部的开发环境中安装它(对于使用框架的用户不须要,只有要编译框架才须要)。

The selected run destination is not valid for this action

有时,Xcode出错并加载了错误的active设置。首先,请尝试重启Xcode。若是错误继续存在,Xcode产生了一个坏的项目(由于Xcode4的一个bug,任何类型的项目都会出现这个问题)。若是是这样,你须要建立一个新项目重来一遍。

连接警告

第一次编译框架target时,Xcdoe会在连接阶段报告找不到文件夹: ld: warning: directory not found for option'-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos' 此时,能够clean并从新编译target,警告会消除。

Core Data momd not found

对于框架项目和应用程序项目,Xcode会以不一样的方式编译momd(托管对象模型文件)。Xcode会简单地在根目录建立.mom文件,而不会建立一个.momd目录(目录中包含VersionInfo.plist和.mom文件)。

这意味着,当从一个embedded framework的model中实例化NSManagedObjectModel时,你必需使用.mom扩展名做为model的URL,而不是采用.momd扩展名。

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@"MyModel" withExtension:@"mom"];

Unknown class MyClass in Interface Builder file.

因为静态框架采用静态连接,linker会剔除全部它认为无用的代码。不幸的是,linker不会检查xib文件,所以若是类是在xib中引用,而没有在O-C代码中引用,linker将从最终的可执行文件中删除类。这是linker的问题,不是框架的问题(当你编译一个静态库时也会发生这个问题)。苹果内置框架不会发生这个问题,由于他们是运行时动态加载的,存在于iOS设备固件中的动态库是不可能被删除的。 有两个解决的办法:

  • 让框架的最终用户关闭linker的优化选项,经过在他们的项目的Other Linker Flags中添加-ObjC和-all_load。

在框架的另外一个类中加一个该类的代码引用。例如,假设你有个MyTextField类,被linker剔除了。假设你还有一个MyViewController,它在xib中使用了MyTextField,MyViewController并无被剔除。你应该这样作:

在MyTextField中:

    • (void)forceLinkerLoad_ {}

在MyViewController中:

+(void) initialize { [MyTextField forceLinkerLoad_]; }

他们仍然须要添加-ObjC到linker设置,但不须要强制all_load了。

  • 这种方法须要你多作一点工做,但却让最终用户避免在使用你的框架时关闭linker优化(关闭linker优化会致使object文件膨胀)。

unexpected file type 'wrapper.cfbundle' in Frameworks &Libraries build phase

这个问题发生在把“假”框架项目做为workspace的依赖,或者把它看成子项目时(“真”框架项目没有这个问题)。尽管这种框架项目产生了正确的静态框架,但Xcode只能从项目文件中看出这是一个bundle,所以它在检查依赖性时发出一个警告,并在linker阶段跳过它。

你能够手动添加一个命令让linker在连接阶段能正确连接。在依赖你的静态框架的项目的OtherLinker Flags中加入:

-framework MyFramework

警告仍然存在, 但不会致使连接失败。

Libraries being linked or not being linked into the finalframework

很不幸, “真”框架和“假”框架模板在处理引入的静态库/框架的工做方式不一样的。

“真”框架模板采用正常的静态库生成步骤,不会连接其余静态库/框架到最终生产物中。

“假”框架模板采用“欺骗”Xcode的手段,让它认为是在编译一个可重定位格式的目标文件,在连接阶段就如同编译一个可执行文件,把全部的静态代码文件连接到最终生成物中(尽管不会检查是否确实目标代码)。为了实现象“真”框架同样的效果,你能够只包含库/框架的头文件到你的项目中,而不须要包含库/框架自己。

Unrecognized selector in (some class with a category method)

若是你的静态库或静态框架包含了一个模块(只在类别代码中声明,没有类实现),linker会搞不清楚,并把代码从二进制文件中剔除。由于在最终生成的文件中没有这个方法,因此当调用这个类别中定义的方法时,会报一个“unrecognizedselector”异常。

要解决这个,在包含这个类别的模块代码中加一个“假的”类。linker发现存在完整的O-C类,会将类别代码连接到模块。

我写了一个头文件 LoadableCategory.h,以减轻这个工做量:

import "SomeConcreteClass+MyAdditions.h"

import "LoadableCategory.h"

MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions);

@implementation SomeConcreteClass(MyAdditions) ... @end

在使用这个框架时,仍然还须要在Build Setting的Other Linker Flags中加入-ObjC。

执行任何代码前单元测试崩溃

若是你在Xcode4.3中建立静态框架(或库)target时,勾选了“withunit tests”,当你试图运行单元测试时,它会崩溃:

Thread 1: EXC_BAD_ACCESS (code=2, address=0x0) 0 0x00000000 --- 15 dyldbootstrap:start(...)

这是lldb中的一个bug。你能够用GDB来运行单元测试。编辑scheme,选择Test,在Info标签中将调试器Debugger从LLDB改成GDB。

 
 
转载自:https://www.gitbook.com/book/leon_lizi/-framework-/details
相关文章
相关标签/搜索