Framework详细与iOS 制作

2021-02-03 16:49:32

参考连接 iOS 制作和使用 Framework

介绍

假如你想将你开发的一些控件或者工具与别人分享,一种方法是直接提供源代码文件。然而,这种方法并不是很优雅。因为它会暴露所有的实现细节,而这些实现你可能并不想开源出来。此外,使用者也可能并不想看到你的所有代码,因为他们可能仅仅只是希望将你的这份代码的一部分植入自己的应用中去使用。

网上关于 Framework 制作的教程不说数不胜数吧,但绝对让人眼花缭乱。但很多都步骤过旧也过简单,更没有更新了,所以这里就把我前些时候总结的 制作Framework的过程记录,重新捋一下。

我用的是Xcode 9.4

正题

首先我们需要打开 Xcode,新建一个项目。 并且选择 Cocoa touch Framework。 如下图

然后随便给这个 Framework 取一个名字,比如 TestSDK

为了区分,分别建立了 PublicFile PrivateFile ProjectFile ,

如下图,并分别拖入到对应的 Header

在你弄清楚之前,这三个组的名称可能会让你迷惑,Public 是你期望的,开源出来的,Private 下的头文件依然是可以暴露出来的,因此名字可能有些误导。奇怪的是,在 Project 下的头文件对你的工程来说才是 “私有” 的,因此,你将会更多地希望你的头文件或者在 Public 下,或者在 Project 下

必须更改的两个注意点:

搜索 mach 将工程改为 静态 。

搜索 Defines Module 修改为NO

最后你还要更改 SDK最低支持 的版本,这个和项目更改 系统支持 的版本一样,不再赘述

到此基本配置基本完成,也实现了项目的闭源

打包(Bundle)资源

导入Bundle

在ImageViewer中使用Bundle

添加自动打包脚本环境,实现自动打包

总结

知识点

1. 库

库是源代码经过编译,形成的二进制代码,别人项目中使用我们的库的时候,库在参与编译的时候,直接link就OK了,按照link的方式,可以把库分为静态库和动态库

2. 静态库

静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里就不会再改变了。
一般以.a 和 .framework为文件后缀名
这种做法是牺牲应用“体量”来节省编译时间。

3. 动态库

与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。
动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。
同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行
以.tbd(之前叫.dylib) 和 .framework 为文件后缀名
苹果系统为我们提供了很多动态链接库,我们可以在我们项目工程中查看一下

4. Framework

Framework 是一种打包方式,将库的二进制文件,头文件和有关的资源文件打包到一起,方便管理和分发。
Framework只是一种打包方式,其本身和静态、动态无关!
但Cocoa Touch Framework 的实际内容为 Header + 动态链接库 + 资源文件

对Framework认识的误区

误区①:.framework是动态库,.a是静态库,前面已经讲过,不再赘述
误区②:有人说“自定义的动态库苹果审核不通过”,那我打的framework是不是通不过审核?
任何没有时间前提的结论都是耍流氓!!!
在 iOS 8 / iOS6之前,iOS 平台不支持使用动态 Framework,开发者可以使用的 Framework 只有系统的framework,这种限制可能是出于安全的考虑
换一个角度讲,因为 iOS 应用都是运行在沙盒当中,不同的程序之间不能共享代码,同时动态下载代码又是被苹果明令禁止的,没办法发挥出动态库的优势,实际上动态库也就没有存在的必要了
但是,码农总是喜欢折腾的,一方面骨子里面有一种“你不让我做我偏要做的倔强”,另一方面用framework确实比用.a加头文件的方式简单,所以这一时期的开发者用了很多“奇淫技巧”来制作framework,这就有了Fake Framework 和 Real Framework的区分
在 iOS 8 / iOS6后,iOS平台添加了动态库的支持,同时, Xcode 6 也原生自带了 Framework 支持,注意,前后两个维度的不同是两件事,不要混淆。。。

那么,为什么 iOS 8 要添加动态库的支持?
主要的理由大概就是 Extension 的出现。Extension 和 App 是两个分开的可执行文件,同时需要共享代码,这种情况下动态库的支持就是必不可少的了。但是这种动态 Framework 和系统的 UIKit.Framework 还是有很大区别;还有就是为了支持swift
虽然同样是动态框架,但是和系统 framework 不同,app 中的使用的 Cocoa Touch Framework 在打包和提交 app 时会被放到 app bundle 中(App 和 Extension 的 Bundle 是共享的),运行在沙盒里,而不是系统中。也就是说,不同的 app 就算使用了同样的 framework,但还是会有多份的框架被分别签名,打包和加载,因此苹果又把这种 Framework 称为 Embedded Framework,也正是代码签名机制,通过AppStore发布的APP是无法通过替换服务端下发framework的方式来进行热更新!

总结:

同一个静态库在不同程序中使用时,每一个程序中都得导入一次,打包时也被打包进去,形成一个程序。而动态库在不同程序中,打包时并没有被打包进去,只在程序运行使用时,才链接载入(如系统的框架如UIKit、Foundation等),所以程序体积会小很多,但是苹果不让使用自己的动态库,否则审核就无法通过。这也是为什么前文将工程选择为 静态的原因

常见的错误:

1, 没有改成静态,不支持动态

2, Defines Module 必须改成NO

3, 支持bitcode的frame操作

1
2
3
4
5
6
7
8
9
如果自己想要制作支持 Bitcode 的 Framework,
1, 工程中开启 Enable Bitcode
2, Build Setting 中 Other Linker Flags 中加入 -fembed-bitcode。
3, Build Setting 中 Other C Flags 中加入 -fembed-bitcode。

作者:山神_云网工作室
链接:https://www.jianshu.com/p/ef5a888e57f2
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

同时支持模拟器真机的配置

静态库和自己项目同时开发的方案

上文中会用到自动打包的 shell 脚本,我使用的是公司其他大神写的根据项目需求配置的脚本,这里就不开源了, 大家可以使用以下脚本,也可以实现自动打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/bin/sh
#要build的target名
TARGET_NAME=${PROJECT_NAME}
if [[ $1 ]]
then
TARGET_NAME=$1
fi
UNIVERSAL_OUTPUT_FOLDER="${SRCROOT}/${PROJECT_NAME}/"

#创建输出目录,并删除之前的framework文件
mkdir -p "${UNIVERSAL_OUTPUT_FOLDER}"
rm -rf "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework"

#分别编译模拟器和真机的Framework
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build
xcodebuild -target "${TARGET_NAME}" ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphonesimulator BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" clean build

#拷贝framework到univer目录
cp -R "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework" "${UNIVERSAL_OUTPUT_FOLDER}"

#合并framework,输出最终的framework到build目录
lipo -create -output "${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${TARGET_NAME}.framework/${TARGET_NAME}" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/${TARGET_NAME}.framework/${TARGET_NAME}"

#删除编译之后生成的无关的配置文件
dir_path="${UNIVERSAL_OUTPUT_FOLDER}/${TARGET_NAME}.framework/"
for file in ls $dir_path
do
if [[ ${file} =~ ".xcconfig" ]]
then
rm -f "${dir_path}/${file}"
fi
done
#判断build文件夹是否存在,存在则删除
if [ -d "${SRCROOT}/build" ]
then
rm -rf "${SRCROOT}/build"
fi
rm -rf "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator" "${BUILD_DIR}/${CONFIGURATION}-iphoneos"
#打开合并后的文件夹
open "${UNIVERSAL_OUTPUT_FOLDER}"


  • 2017-09-11 09:22:09

    nginx配置返回文本或json

     有些时候请求某些接口的时候需要返回指定的文本字符串或者json字符串,如果逻辑非常简单或者干脆是固定的字符串,那么可以使用nginx快速实现,这样就不用编写程序响应请求了,可以减少服务器资源占用并且响应性能非常快

  • 2017-09-11 11:30:09

    linux 获取经过N层Nginx转发的访问来源真实IP

    通常情况下我们使用request.getRemoteAddr()就可以获取到客户端ip,但是当我们使用了nginx作为反向代理后,由于在客户端和web服务器之间增加了中间层,因此web服务器无法直接拿到客户端的ip,通过$remote_addr变量拿到的将是反向代理服务器的ip地址。如果我们想要在web端获得用户的真实ip,就必须在nginx这里作一个赋值操作,如下:

  • 2017-09-11 16:15:11

    Nginx日志管理

    通过访问日志,你可以得到用户地域来源、跳转来源、使用终端、某个URL访问量等相关信息;通过错误日志,你可以得到系统某个服务或server的性能瓶颈等。因此,将日志好好利用,你可以得到很多有价值的信息。

  • 2017-09-11 16:34:14

    Nginx如何保留真实IP和获取前端IP

    squid,varnish以及nginx等,在做反向代理的时候,因为要代替客户端去访问服务器,所以,当请求包经过反向代理后,在代理服务器这里这个IP数据包的IP包头做了修改,最终后端web服务器得到的数据包的头部的源IP地址是代理服务器的IP地址,这样一来,后端服务器的程序给予IP的统计功能就没有任何意义,所以在做代理或集群的时候必须解决这个问题,这里,我以nginx做集群或代理的时候如何给后端web服务器保留(确切的说是传递)客户端的真实IP地址。

  • 2017-09-11 16:35:22

    ngx_http_realip_module使用详解

    网络上关于ngx_http_realip_module的文章千篇一律,全是在说怎么安装,最多贴一个示例配置,却没有说怎么用,为什么这么用,官网文档写得也十分简略,于是就自己探索了一下。

  • 2017-09-11 16:39:43

    基于Nginx dyups模块的站点动态上下线

    在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一定的轮询策略,将请求路由到后端一台指定的服务器上。

  • 2017-09-13 13:49:21

    Web性能测试:工具之Siege详解

    Siege是一款开源的压力测试工具,设计用于评估WEB应用在压力下的承受能力。可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行。siege可以从您选择的预置列表中请求随机的URL。所以siege可用于仿真用户请求负载,而ab则不能。但不要使用siege来执行最高性能基准调校测试,这方面ab就准确很多