iOS - Umbrella Header在framework中的应用

在相对较大的互联网App研发中, framework静态库被广泛应用, 那么在研发framework的时候也产生好多的问题? 其实一个常见的问题的就是umbreall header文件的使用。

大家是否有遇到过一个常见的错误使用umbrella header的场景: framework的文件明明被主工程引用了, 但是在编译的时候依旧抛出<font color='orange'>Lexical or Preprocessor Issue - Umbrella header for module 'xxx' does not include header 'xxx.h'</font>的警告。

那么问题来了, 什么是umbrella header? 什么又是umbrella framework呢?

参考官方文档《Introduction to Framework Programming Guide》学习说明, 可以了解到Framework区分Standard Framework和Umbrella Framework。但是, 我找了半天也没有找到官方文档有对Umberlla framework给出明确的定义(大家找到了记得告诉我)。在官方文档《Anatomy of Framework Bundles》章节中, 我找到三段比较合理说明Umbrella Framework的话:

Umbrella frameworks add minor refinements to the standard framework structure, such as the ability to encompass other frameworks


The structure of an umbrella framework is similar to that of a standard framework, and applications do not distinguish between umbrella frameworks and standard frameworks when linking to them. However, two factors distinguish umbrella frameworks from other frameworks. The first is the manner in which they include header files. The second is the fact that they encapsulate subframeworks.


Physically, umbrella frameworks have a similar structure to standard frameworks. One significant difference is the addition of a Frameworks directory to contain the subframeworks that make up the umbrella framework.

字面上的意思应该是在标准的Framework做了一些改良的工作, 使其可以嵌套包含Framework。
在物理结构上, Umbrella Framework只在包含头文件的方式以及是否包含子Framework和普通的Framework存在区别。

那么引用头文件的地方又有什么区别呢? 还是参考官方文档引用:

For most frameworks, you can include header files other than the master header file. You can include any specific header file you want as long as it is available in the framework’s Headers directory. However, if you are including an umbrella framework, you must include the master header file. Umbrella frameworks do not allow you to include the headers of their constituent subframeworks directly. See Restrictions on Subframework Linking for more information.

简单翻译一下: 普通的framework可以通过引用对应的heaedr文件而不是Master Header File去引用需要使用的类, 只需要对应的header头文件在Headers文件夹下暴露, 并没有强制要求引用Master Header File。Umbrella Framework要求必须要引用Master Header File, 并且头文件中不能直接引用子Framework的东西。

上述描述已经说了Umbrella Framework一定要引用Master Header File, 而Umbrella Framework的Master Header File就是Umbrella header文件。

大家是否存在一个疑问, 官方说明中只有强制规定一定要引用Umbrella Header文件, 但是却没有说能不能单独引用Umbrella Framework的其他头文件呢? 我们可以自己试验一下:

  1. 在Umbrella Framework新建一个testObject类, 分别产生了testObject.htestObject.m文件。
  2. 打开Framework配置文件, 在Build PhasesHeaders里的Public目录下, 将testObject.h文件添加进去。
  3. Build Framework看是否报错。
  4. 在主工程中调用初始化testObject对象, 看编译是否报错。

执行结果:

  • 步骤3: 没有编译报错, 但是报出了<font color='orange'>Lexical or Preprocessor Issue - Umbrella header for module 'STDemoUI' does not include header 'testObject.h'</font>的警告。
  • 步骤4: 执行正常。

那么我们来总结一下:

  1. Standard Framework不能包含Sub Framework; Umbrella Framework可以包含子Framework;
  2. Standard Framework可以直接引用需要使用的头, 也可以通过引用Master Header file来引用需要使用的类; Umbrella Framework需要通过引用Master Header File(Umbrella Header)来引用需要使用的类;

规范的写法

Umbrella Framework默认会创建一个同名.h文件最为Umbrella Header文件。规范的写法当然是遵从默认的模式, 将所有需要暴露的头文件都写在Umbrella Header文件中。

例如: STDemoUI.framework工程包含了STClassOne、STClassTwoSTClassThree三个类。STDemoUI会生成一个默认的伞头文件(直译Umbrella Header, 不专业)STDemoUI.h。假设该framework的三个类均需要在外部调用使用, 则STDemoUI.h需要将三个类的引用均写入伞头文件中。

// STDemoUI.h
// ...

#import <STDemoUI/STClassOne.h>
#import <STDemoUI/STClassTwo.h>
#import <STDemoUI/STClassThree.h>

在需要调用的主工程中, 仅仅只要将Umbrella Header引用即可调用所有在Umbrella Header中包含的类了。

// 在主工程需要应用的类中包含Umbrella Header
#import <STDemoUI/STDemoUI.h>

如何重命名umbrella header

如果大家都遵从默认的Umbrella Framework的写法, 在同名头文件中写需要暴露的引用头文件, 那么就不需要考虑怎么重命名Umbrella header了。

很多时候, 理想和现实是有差距的, 程序员写代码多数是在二次接手进行开发的。假设公司的前辈已经将Framework的同名文件用作了一个逻辑类, 给同名文件创建了.m文件, 并已经书写了逻辑并应用了各个工程里面去了。那么显然迁移头文件功能代码是不可能的, 因为很多依赖该Framework的业务部门都需要针对库进行代码优化。

在这种不能将同名文件作为Umbrella header的情况下, 我们又不想通过Public强制暴露头文件的情况下(不写在Umbrella Header中会有警告)。我们就需要对Umbrella Header进行指定了。

指定Umbrella Header入口在哪里呢?

万事开头难, 我们想要指定Umbrella Header, 但是在哪里指定呢? 通过文章搜索, 都没有找到一个比较合理的方案, 那就只能自己摸索了。

  1. 在工程全局搜索umbrella关键字 - <font color='red'>Failed</font>
  2. 在Build Settings里搜索umbrella关键字 - <font color='red'>Failed</font>
  3. 在打包好的STDemoUI.framework中搜索umbrella关键字 - <font color='green'>Bingo</font>

既然搜索到了关键字就好办了, 双击点开STDemo.framework, 我们可以看到如下图所示五个文件(夹)。

Framework内部结构

初略看名称可以推测出每个文件以及文件夹所承担的作用:

  • _CodeSignature: 保存签名相关文件
  • Headers: framework暴露的所有头文件
  • Info.plist: 描述了该framework所包含的项目配置信息
  • STDemo: 编译后的核心库文件
  • Modules: ??橄喙匚募? 目测只包含了module.modulemap文件

我们在module.modulemap文件中找到了umbrella关键字。文件内容如下:

framework module STDemoUI {
  umbrella header "STDemoUI.h"

  export *
  module * { export * }
}

原来Framework的umbrella header是在这个位置被指定的, 但是这个已经是编译好的工程, 我们总不能每次编译好了再进到包里面修改下。既然我们已经找到umbrella header是在module中去指定, 那么我们就用module作为关键字再去Build Settings里重新搜索下呗~

这回我们在Kernel ModulePackaging中均找到了Module关键字, 在Packaging标签中, 有一项Module Map File属性, 看名字应该是用来指定modulemap文件的, 这不解决了么?

指定Modulemap文件

找到了解决方案, 那么接下来就要动手去解决问题了

  1. 创建一个新的.h文件; ex: STHeader.h

    • 将所有需要暴露的头文件均写入STHeader.h
  2. 创建一个新的modulemap文件; ex: stdemoalt.modulemap

  3. 在新的modulemap中指定umbrella header

    framework module STDemoUI {
        umbrella header "STHeader.h"
        
        export *
        module * { export * }
    }
    
  4. 在framework的Build Settings中的Module Map File指定新建的modulemap文件

好了, 准备工作完毕, CMD+B, 编译完了, 打开framework包中的Module文件夹, 看是否包含了新指定的modulemap。

抛出两个疑问:

  1. Module是什么?
  2. 如果Defines Module指定为NO, 那会发生什么事情呢?

题外话

这里我要引用官网Guidelins for Creating Frameworks的一个子标题Don't Create Umbrella Frameworks。原文如下:

While it is possible to create umbrella frameworks using Xcode, doing so is unnecessary for most developers and is not recommended. Apple uses umbrella frameworks to mask some of the interdependencies between libraries in the operating system. In nearly all cases, you should be able to include your code in a single, standard framework bundle. Alternatively, if your code was sufficiently modular, you could create multiple frameworks, but in that case, the dependencies between modules would be minimal or nonexistent and should not warrant the creation of an umbrella for them

在大多数情况下, 苹果是不建议手动创建Umbrella Framework。

总结

本文简单的梳理了官方文章关于Umbrella Framework和Umbrella Header的介绍说明, 产生警告的原因是没有引用umbrella header或者暴露头没有写在umbrella header中。在umbrella header被已使用的前提下, 本文提供了一种通过重命名Umbrella Header文件的方式来消除警告的解决方案。

虽然引用警告可以被消除, 但是建议大家还是采用规范的做法: 尽量不要在同名头文件中写业务逻辑代码, 用同名文件作为Umbrella库的Master Header File。

水平有限, 有错误请大家及时指出哈~
转载请注明出处哦~

参考文献:

  1. https://developer.apple.com/library/ios/documentation/MacOSX/Conceptual/BPFrameworks/Frameworks.html
  2. http://stackoverflow.com/questions/32153131/renaming-umbrella-header/32153383#32153383
最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事?!?“怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • 静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别...
    吃瓜群众呀阅读 11,918评论 3 42
  • 如果你想将你开发的控件与别人分享,一种方法是直接提供源代码文件。然而,这种方法并不是很优雅。它会暴露所有的实现细节...
    小热狗阅读 3,470评论 3 9
  • 介绍 动态库形式:.dylib和.framework 静态库形式:.a和.framework 动态库和静态库的区别...
    齐滇大圣阅读 46,187评论 18 252
  • 原文链接在上一篇教程(中译版)中,你学到了怎么样创建一个可复用的圆形旋钮控件。然而你可能不清楚怎样让其他开发者更方...
    _冷忆阅读 2,444评论 9 5