一、umbrella framework

  • 将多个已经封装好的 framework 封装成一个,封装的这种 framework 就是 umbrella framework。
  • Apple 的官方文档中明确提到了不建议自己去创建 umbrellaframework,Apple 的 Guidelins for Creating Frameworks 的官方说明:
    Don’t Create Umbrella FrameworksWhile 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
  • 创建并使用 UmbrellaFramework 分为三部分:
    • SubFramework:创建一个基础 framework;
    • UmbrellaFramework:framework 里封装 framework;
    • UmbrellaFrameworkDemo:使用 demo。

二、创建一个基础的 framework

  • 创建一个 framework 工程:Subframework;

  • 添加 SubSayHello 类,添加 sayHello 方法;
    @interface SubSayHello : NSObject- (void)sayHello;@end@implementation SubSayHello- (void)sayHello {NSLog(@"say Hello");}@end
  • 在 SubFramework.h 头文件中导入 SubSayHello.h:
    #import <Subframework/SubSayHello.h>
  • 将 SubSayHello.h 添加到 Target -> Build Phases -> Headers -> Public,可手动拖拽;
  • Build Settings -> Mach-O Type 选择 Static Library 静态库;
  • 生成通用 framework:
    • 方式一:分别在模拟器和真机下编译工程,生成两个 framework,用命令行合并成一个通用的(注意:如果执行命令报错,可以将结果地址改为 /Users/yydw/Desktop/Subframework.xx,生成后再将后缀名去掉):
    $ lipo -create [真机 Framework 二进制文件路径] [模拟器 Framework 二进制文件路径] -output [结果路径]

    $ lipo -create /Users/ydw/Library/Developer/Xcode/DerivedData/Subframework-hkwchwbjmtuhoseinwkzbtcjxpbj/Build/Products/Debug-iphoneos/Subframework.framework/Subframework /Users/ydw/Library/Developer/Xcode/DerivedData/Subframework-hkwchwbjmtuhoseinwkzbtcjxpbj/Build/Products/Debug-iphonesimulator/Subframework.framework/Subframework -output /Users/ydw/Desktop/Subframework
    • 方式二:脚本生成
    • 为 SubFramework 工程添加 Target -> Aggregate:

  • 在新添加的 Target 中添加脚本:

  • 脚本内容:
    # Sets the target folders and the final framework product.FRAMEWORK_NAME=LibraryNameFRAMEWORK_VERSION=1.0FRAMEWORK_CONFIG=Release# Install dir will be the final output to the framework.# The following line create it in the root folder of the current project.INSTALL_PATH=${PROJECT_DIR}/Products/INSTALL_DIR=${INSTALL_PATH}/${FRAMEWORK_NAME}.framework# Working dir will be deleted after the framework creation.WORK_DIR=buildDEVICE_DIR=${WORK_DIR}/${FRAMEWORK_CONFIG}-iphoneos/${FRAMEWORK_NAME}.frameworkSIMULATOR_DIR=${WORK_DIR}/${FRAMEWORK_CONFIG}-iphonesimulator/${FRAMEWORK_NAME}.frameworkxcodebuild -configuration "${FRAMEWORK_CONFIG}" -target "${FRAMEWORK_NAME}" -sdk iphoneosecho "Build simulator"xcodebuild -configuration "${FRAMEWORK_CONFIG}" -target "${FRAMEWORK_NAME}" -sdk iphonesimulator# Creates install directory if it not exits.if [ ! -d "${INSTALL_DIR}" ]thenmkdir -p "${INSTALL_DIR}"fi# Creates headers directory if it not exits.if [ ! -d "${INSTALL_DIR}/Headers" ]thenmkdir -p "${INSTALL_DIR}/Headers"fi# Remove all files in the headers diectory.for file in `ls "${INSTALL_DIR}/Headers"`dorm "${INSTALL_DIR}/Headers/${file}"done# Remove binary library file.rm -f ${INSTALL_DIR}/${FRAMEWORK_NAME}# Copies the headers files to the final product folder.if [ -d "${DEVICE_DIR}/Headers" ]thenfor file in `ls "${DEVICE_DIR}/Headers"`docp "${DEVICE_DIR}/Headers/${file}" "${INSTALL_DIR}/Headers/${file}"donefi# copy nibs to bundle,then copy bundle to final folderBUNDLE_DIR=${DEVICE_DIR}/${FRAMEWORK_NAME}.bundleif [ -d "${BUNDLE_DIR}" ];thenif ls ${DEVICE_DIR}/*.nib >/dev/null 2>&1;thenrm -rf ${BUNDLE_DIR}/*.nibcp -rf ${DEVICE_DIR}/*.nib ${BUNDLE_DIR}firm -rf "${INSTALL_DIR}/${FRAMEWORK_NAME}.bundle"cp -R "${BUNDLE_DIR}" "${INSTALL_DIR}/${FRAMEWORK_NAME}.bundle"fiecho "Merge with simulator"# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.lipo -create "${DEVICE_DIR}/${FRAMEWORK_NAME}" "${SIMULATOR_DIR}/${FRAMEWORK_NAME}" -output "${INSTALL_DIR}/${FRAMEWORK_NAME}"open "${INSTALL_PATH}"# rm -r "${WORK_DIR}"
  • 查看 framework 支持的架构:
    $ lipo -info [framework 二进制文件路径]

三、framework 里封装 framework

  • 创建 Cocoa Touch Framework 工程 UmbrellaFramework;
  • 导入 SubFramework:

  • 选择 Target -> Build Phases -> 点击左上角+号 -> New Copy Files Phase 添加 Copy Files,将 SubFramework 添加到 Copy Files,选择 Destination 为 Frameworks:

  • 添加 UmbrellaSayHello 类,添加 sayHello 方法,并在 sayHello 方法中调用 SubFramework 的 sayHello 方法:
    @interface UmbrellaSayHello : NSObject- (void)sayHello;@end#import <Subframework/SubSayHello.h>@implementation UmbrellaSayHello- (void)sayHello {NSLog(@"%s", __func__);SubSayHello *ssh = [[SubSayHello alloc] init];[ssh test];}@end
  • UmbrellaFramework.h 头文件中导入将 UmbrellaSayHello.h:
    #import <UmbrellaFramework/UmbrellaSayHello.h>
  • 将 UmbrellaSayHello.h 添加到 UmbrellaFramework 的公共 headers 中;
  • Architectures 添加 armv7s;
  • 连接选项 Mach-O Type 不用改,选择默认选项 Dynamic Library,这意味着外层的 UmbrellaFramework 是一个动态库。
  • 生成真机和模拟器都能用的 framework,详情请参考:iOS之深入解析构建动态库与framework动态更新。

四、使用 UmbrellaFramework

  • 创建工程 UmbrellaFrameworkDemo;
  • 嵌入UmbrellaFramework,选择工程 -> General -> Embedded binaries,添加UmbrellaFramework。UmbrellaFramework 将会同时添加到 Linked Frameworks and Libraries:

  • 工程中使用:
    #import <UmbrellaFramework/UmbrellaFramework.h>- (void)viewDidLoad {[super viewDidLoad];UmbrellaSayHello *ush = [[UmbrellaSayHello alloc] init];[ush sayHello];}

五、UmbrellaHeader

① 什么是 umbrella header?

  • framework 的文件明明被主工程引用了,但是在编译的时候依旧抛出下面的警告:
    Lexical or Preprocessor Issue - Umbrella header for module 'xxx' does not include header 'xxx.h'
  • 参考官方文档《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 frameworksThe 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 的其他头文件呢? 我们可以自己试验一下:
    • 在 Umbrella Framework 新建一个 testObject 类,分别产生了 testObject.h 和 testObject.m 文件。
    • 打开 Framework 配置文件,在 Build Phases 的 Headers 里的 Public 目录下,将 testObject.h 文件添加进去。
    • Build Framework 看是否报错。
    • 在主工程中调用初始化 testObject 对象,看编译是否报错。
  • 执行结果:
    • 上面的第三步没有编译报错, 但是报出了 Lexical or Preprocessor Issue - Umbrella header for module ‘STDemoUI’ does not include header ‘testObject.h’ 的警告。
    • 上面的第四步执行正常。
  • 总结一下:
    • Standard Framework 不能包含 Sub Framework;Umbrella Framework 可以包含子 Framework;
    • Standard Framework 可以直接引用需要使用的头,也可以通过引用 Master Header file 来引用需要使用的类;Umbrella Framework 需要通过引用 Master Header File(Umbrella Header) 来引用需要使用的类。

② 规范写法

  • Umbrella Framework 默认会创建一个同名 .h 文件做为 Umbrella Header 文件。规范的写法当然是遵从默认的模式,将所有需要暴露的头文件都写在 Umbrella Header 文件中。
  • 例如:YDWDemoUI.framework 工程包含了 YDWClassOne、YDWClassTwo 和 YDWClassThree 三个类。YDWDemoUI 会生成一个默认的伞头文件(直译 Umbrella Header)YDWDemoUI.h。假设该 framework 的三个类均需要在外部调用使用,则 YDWDemoUI.h 需要将三个类的引用均写入伞头文件中。
    // YDWDemoUI.h// ...#import <YDWemoUI/YDWClassOne.h>#import <YDWemoUI/YDWClassTwo.h>#import <YDWemoUI/YDWClassThree.h>
  • 在需要调用的主工程中, 仅仅只要将 Umbrella Header 引用即可调用所有在 Umbrella Header 中包含的类:
    // 在主工程需要应用的类中包含Umbrella Header#import <YDWDemoUI/YDWDemoUI.h>

③ 重命名 umbrella header

  • 如果大家都遵从默认的 Umbrella Framework 的写法,在同名头文件中写需要暴露的引用头文件,那么就不需要考虑怎么重命名 Umbrella header 了。
  • 很多时候,理想和现实是有差距的,程序员写代码多数是在二次接手进行开发的。假设公司的前辈已经将 Framework 的同名文件用作了一个逻辑类, 给同名文件创建了 .m 文件, 并已经书写了逻辑并应用了各个工程里面去了。那么显然迁移头文件功能代码是不可能的,因为很多依赖该 Framework 的业务部门都需要针对库进行代码优化。
  • 在这种不能将同名文件作为 Umbrella header 的情况下,我们又不想通过 Public 强制暴露头文件的情况下(不写在 Umbrella Header 中会有警告),就需要对 Umbrella Header 进行指定。
  • 指定 Umbrella Header 入口在哪里呢?
    • 在工程全局搜索 umbrella 关键字 - Failed;
    • 在 Build Settings 里搜索umbrella关键字 - Failed;
    • 在打包好的 YDWDemoUI.framework 中搜索 umbrella 关键字 - Success;
  • 双击点开 YDWDemo.framework,内容如下:

  • 初略看名称可以推测出每个文件以及文件夹所承担的作用:
    • _CodeSignature:保存签名相关文件
    • Headers:framework 暴露的所有头文件
    • Info.plist:描述了该 framework 所包含的项目配置信息
    • UmbrellaFramework:编译后的核心库文件
    • Modules:模块相关文件夹,目测只包含了 module.modulemap 文件
    • Frameworks:包含的子 framework
  • 在 module.modulemap 文件中找到了 umbrella 关键字。文件内容如下:
    framework module YDWDemoUI {umbrella header "YDWDemoUI.h"export *module * { export * }}
  • 原来 Framework 的 umbrella header 是在这个位置被指定的,但是这个已经是编译好的工程, 总不能每次编译好了再进到包里面修改下,既然已经找到 umbrella header 是在 module 中去指定,那么就用 module 作为关键字再去 Build Settings 里重新搜索下。
  • 这回在 Kernel Module 和 Packaging 中均找到了 Module 关键字,在 Packaging 标签中,有一项 Module Map File 属性,看名字应该是用来指定 modulemap 文件的。

  • 指定 Modulemap 文件
    • 创建一个新的 .h 文件,如:YDWHeader.h。将所有需要暴露的头文件均写入 YDWHeader.h;
    • 创建一个新的 modulemap 文件,如:stdemoalt.modulemap;
    • 在新的 modulemap 中指定 umbrella header。
    framework module YDWDemoUI {umbrella header "YDWHeader.h"export *module * { export * }}
    • 在 framework 的 Build Settings 中的 Module Map File 指定新建的 modulemap 文件;
    • CMD+B 编译,打开 framework 包中的 Module 文件夹,看是否包含了新指定的 modulemap。

④ 总结

  • 简单地梳理了官方文章关于 Umbrella Framework 和 Umbrella Header 的介绍说明,产生警告的原因是没有引用 umbrella header 或者暴露头没有写在 umbrella header 中。在 umbrella header 被已使用的前提下,本文提供了一种通过重命名 Umbrella Header 文件的方式来消除警告的解决方案。
  • 虽然引用警告可以被消除,但是建议大家还是采用规范的做法:尽量不要在同名头文件中写业务逻辑代码, 用同名文件作为 Umbrella 库的 Master Header File。

iOS之深入解析UmbrellaFramework的封装与应用相关推荐

  1. iOS之深入解析消息转发objc_msgSend的应用场景

    一.消息转发 现有如下示例: id o = [NSObject new]; [o lastObject]; 执行上面代码,程序会崩溃并抛出以下异常: [NSObject lastObject]: un ...

  2. iOS之深入解析WKWebView加载的生命周期与代理方法

    一.前言 从 WebView 开始加载一条请求,到页面完整呈现这一过程发生了什么?无论是做 WebView 性能优化还是异常问题监控与排查,都离不开对WKWebView加载的生命周期与代理方法的剖析. ...

  3. iOS中XML解析汇总

    在时间上TBXML占优,libxml2支持了边下载边解析. 来源:http://www.codeios.com/forum.php?mod=viewthread&tid=9880&hi ...

  4. iOS中XML解析 (一) TBXML (实例:打印xml内容及存储到数组)

    关联:iOS中XML解析 (二) libxml2(实例:打印xml内容及存储到数组) 在时间上TBXML占优,libxml2支持了边下载边解析. 来源:http://www.codeios.com/f ...

  5. iOS事件全面解析 (触摸事件、手势识别、摇晃事件、耳机线控)

    -- iOS事件全面解析 概览 iPhone的成功很大一部分得益于它多点触摸的强大功能,乔布斯让人们认识到手机其实是可以不用按键和手写笔直接操作的,这不愧为一项伟大的设计.今天我们就针对iOS的触摸事 ...

  6. iOS基础-数据解析方法初步总结-(XML,JSON欢迎指正)

    小时候的记忆与大家一起分享,愿君安好! 解析的基本概念 解析: 从事先规定好的格式中提取数据 解析的前提: 提前约定好格式,数据提供方按照格式提供数据,数据获取方则按照格式获取数据 OS开发常见的解析 ...

  7. iOS swift Alamofire+HandyJSON网络框架封装

    iOS swift Alamofire+HandyJSON网络框架封装 我们在学习Objective_C时使用的网络框架是AFNetworking+MJExtension,而在swift中Alamof ...

  8. 一劳永逸,iOS 网页视图控制器通用类封装

    原文链接:http://www.jianshu.com/p/553424763585 随着 H5 的发展,在 iOS 开发中,网页视图的使用率逐渐提升,为了增加代码封装度.减轻开发负担,因此通常会对网 ...

  9. iOS之深入解析dyld与ObjC关联的底层原理

    App启动与dylb加载 我们知道 dyld 的加载过程,即在 App 启动启动执行 main 函数之前,dylb 主要作了环境变量配置.共享缓存.主程序的初始化.插入动态库.链接主程序.链接动态库. ...

最新文章

  1. MongoDB工具最新开发 源代码更新 兼 进展报告 - 集群功能开发
  2. 小米视频加载进度条效果实现
  3. Myeclipse的破解步骤
  4. 解复用FLV文件(基于FFMPEG解析FLV(h264+aac))
  5. InstallShield高级应用--获取机机所有ORACLE服务列表
  6. iOS获取自1970年毫秒数使用OC和swift两种语法,返回字符串
  7. three.js script vertex和fragment在react中使用/纯js写法
  8. r语言 rep(c(1,R语言(一)
  9. 做自媒体培训赚的盆满钵满
  10. ColorPic 一套簡單好用的顏色選擇器!
  11. 一文读懂如何做好生产计划排程
  12. 解决树莓派aplay找不到声卡的问题
  13. vit transformer中的cls_token
  14. Android之bugly使用
  15. javac: 无效的标记
  16. VISIO同时选中多条线
  17. Vue--移动端开发
  18. 水仙花数的判断(JAVA)
  19. python能代替cad吗_免费且强大的CAD软件_DraftSight V1R5.1 可直接用来代替autoCAD
  20. Spring——IOC(Inversion of Control)容器(2)

热门文章

  1. Oracle分区表 (二)
  2. MySQL远程连接丢失问题解决方法(Lost connection to MySQL server)
  3. 前端--3、JavaScript
  4. ORACLE 外部表的简单使用
  5. JS中的事件冒泡——总结
  6. 跟燕十八学习PHP-第十九天-热身项目完善
  7. java atomicinteger_Java多线程进阶(十三)—— J.U.C之atomic框架:AtomicInteger
  8. CSS样式为什么放在head中,而不放在body底部
  9. C语言学习之通过指针变量调用它所指向的函数
  10. android 开发卫星菜单,android之类似卫星菜单,来自定义ViewGroup。。。。。