Xcode 6制作动态及静态Framework

http://www.cocoachina.com/ios/20141126/10322.html

有没有写SDK或者要将一些常用的工具类做成Framework的经历? 你或许自己写脚本完成了这项工作,相信也有很多的人使用 iOS-Universal-Framework ,随着Xcode 6的发布,相信小伙伴们已经都知道了,Xcode 6支持做Framework了. 同时iOS-Universal-Framework开发者也宣布不在继续维持此项目的开发,建议开发者使用Xcode 6制作,目前网上也有很多制作iOS Framework的资料,但大多都不够详细,接下来本文会详情介绍一下在Xcode 6下制作iOS Framework.

关于静态库和动态库的概念,网上资料很多,这里不做叙述,只讲解制作过程。

创建iOS动态库

新建工程并选择默认Target为Cocoa Touch Framework, 如图:

做编码工作,在这里我简单的写了一个Utils的类,并写了一个log方法

设置开放的头文件:Framework中有些类可能是一些私有的辅助工具,不需要使用者看到,在这里只需要把开放出去的类放到Public下, 如图

这样生成的Framework的Headers目录下也只能看到Public的头文件

编码完成之后,直接Run就能成功生成Framework文件了,选择 xCode->Window->Organizer->Projects->Your Project, 打开工程的Derived Data目录,这样就能找到生成的Framework文件了,如图

新建测试工程,使用生成的Framework

将Framework文件导入到测试工程,调用Framework中的代码

1
2
MyUtils *utils = [MyUtils new]; 
[utils log:@"didFinishLaunchingWithOptions"];

运行报错(Reason: Image Not Found)

为什么会这样的?因为我们做的是动态库,在使用的时候需要额外加一个步骤,要把Framework同时添加到‘Embedded Binaries’中

注意: 在XCode 6之前是没有这个选项的(我没发现),所以理论上XCode 5及之前的版本无法使用Xcode 6下生成的Framework动态库。

到这里,假定你整个过程都是使用的模拟器做的,那看上去会很顺利。这时候尝试将测试工程部署到真机上,问题来了

ld: warning: ignoring file /work/ios/MyFrameworkTest/MyFrameworkTest/MyFramework.framework/MyFramework, file was built for x86_64 which is not the architecture being linked (armv7): /work/ios/MyFrameworkTest/MyFrameworkTest/MyFramework.framework/MyFramework

Undefined symbols for architecture armv7:

"_OBJC_CLASS_$_MyUtils", referenced from:

objc-class-ref in AppDelegate.o

ld: symbol(s) not found for architecture armv7

clang: error: linker command failed with exit code 1 (use -v to see invocation)

为什么会这样?错误提示已经很明显了,因为我们制作动态库的时候,选的设备是模拟器,如果选真机的话,那生成的库也只能在真机上使用,那我们该怎样制作一个通用的动态库呢? 简单的方法是分别生成模拟器和真机上运行的库,然后在合并,这个方法,在每次生成动态库的时候,过程都会很繁琐,下面我们用一个脚本来自动完成它。

制作通用动态库

新建Aggregate Target

添加script到新建的Target

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
# Sets the target folders and the final framework product.
# 如果工程名称和Framework的Target名称不一样的话,要自定义FMKNAME
# 例如: FMK_NAME = "MyFramework"
FMK_NAME=${PROJECT_NAME}
# Install dir will be the final output to the framework.
# The following line create it in the root folder of the current project.
INSTALL_DIR=${SRCROOT}/Products/${FMK_NAME}.framework
# Working dir will be deleted after the framework creation.
WRK_DIR=build
DEVICE_DIR=${WRK_DIR}/Release-iphoneos/${FMK_NAME}.framework
SIMULATOR_DIR=${WRK_DIR}/Release-iphonesimulator/${FMK_NAME}.framework
# -configuration ${CONFIGURATION}
# Clean and Building both architectures.
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphoneos clean build
xcodebuild -configuration "Release" -target "${FMK_NAME}" -sdk iphonesimulator clean build
# Cleaning the oldest.
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
# Uses the Lipo Tool to merge both binary files (i386 + armv6/armv7) into one Universal final product.
lipo -create "${DEVICE_DIR}/${FMK_NAME}" "${SIMULATOR_DIR}/${FMK_NAME}" -output "${INSTALL_DIR}/${FMK_NAME}"
rm -r "${WRK_DIR}"
open "${INSTALL_DIR}"

选中新建的Target,Run, 如果没有异常的话,会自动弹出生成的Framework文件

这样生成的动态库就能同时支持模拟器和真机了。

Xcode 6下制作通用静态库

上面我们也提到了,这样生成的动态库恐怕很难在Xcode 5上使用,那我们为什么非要用动态库呢,一般情况下不是用静态库就好了吗? So Easy!只需要修改一个参数即可生成静态库了。

使用静态库的话,就可以把Framework从‘Embedded Binaries’中删除了. 亲测在Xcode 5下可用。把新生成的库导入到测试工程,试试在模拟器和真机上运行,一切OK.

不巧,如果你用的真机是iPhone5 C, 那悲剧又要发成了,生成的Framework竟然不支持armv7s,不知是Xcode 6的bug,还是因为苹果认为使用armv7s的设备太少,可以不支持了.Xcode 新建工程,默认的Architectures竟然不包含armv7s.

想要生成的库支持armv7s,把armv7s添加到Architectures中,重新生成Framework即可

判断一个Framework支持哪些架构

我们该怎么验证生成的Framework支持哪些平台呢,总不能一个个测试吧?当然不用.下面的命令是加上armv7s前后生成的framework的对比

1
2
3
4
Yearsdembp:Products Years$ lipo -info ./MyFramework.framework/MyFramework 
Architectures in the fat file: ./MyFramework.framework/MyFramework are: i386 x86_64 armv7 arm64 
Yearsdembp:Products Years$ lipo -info ./MyFramework.framework/MyFramework 
Architectures in the fat file: ./MyFramework.framework/MyFramework are: armv7 armv7s i386 x86_64 arm64
 

4、建立一个真机和模拟器通用的framework

首先用finder找到framework所在的位置

然后找到framework中的文件,例如这里的  Kalagame-library,并且纪录其路径  os_frame_path

同样方法打开另一个文件夹,纪录其中库的路径,simulator_frame_path

然后打开控制台,输入 lipo -create os_frame_path  simulator_frame_path  -output  newframe

这样就完成了模拟器和真机版本framework的合并,用finder找到这个newframe,然后把newframe改名字(例如这里的Kalagame-library),并放回到framework文件夹中,替换原来的文件。

例如:

1. lipo -create /Users/chengdeluo/Library/Developer/Xcode/DerivedData/MyFramework-dkfgbkcpzmnceoenwrpehqyoopof/Build/Products/Debug-iphoneos/MyFramework.framework/MyFramework  /Users/chengdeluo/Library/Developer/Xcode/DerivedData/MyFramework-dkfgbkcpzmnceoenwrpehqyoopof/Build/Products/Debug-iphonesimulator/MyFramework.framework/MyFramework  -output /Users/chengdeluo/Desktop/newFramework

2. 接下来用finder找到这个newFramework,然后把newframe改名字为:MyFramework ,并放回到framework文件夹中,替换原来的(MyFramework)文件。

使用框架时的各种坑 

1. 如果框架本身要引用到.dylib(动态链接库), (通常会报错: include of non=modular header inside framework 'Hbb_FileFramework.unzip')那么你需要做如下配置:

应用工程需要配置 allows non-modular includes in framework modules 为YES

2. 如果你的静态frameowrk/静态库(.a)中包含有category文件(如:NSString+StackSymbol.h)

那么你恭喜你要继续配置,就是这么麻烦, 但是没办法

应用工程 project -> target -> build settings -> linker -> other linker flags

中添加 -force_load $(SOURCE_ROOT)/UseOfHbb_LogDemo/Hbb_Log.framework/Hbb_Log

注意: 这里是加载框架Hbb_Log.framework里的名为Hbb_Log的文件, 你可以看成框架源代码的打包文件

3. 如果框架工程中警告:

Error: “File was built for archive which is not the architecture being linked (armv7s)”

那么只是叫你把真机和模拟器生成的工程合并起来

4. 如果框架工程中出现declaration of must be imported from module before it is required

或者 duplicate file.. 等编译错误的话, 很可能框架工程本身引用到框架本身, 照成定义重复.

我们这种情况常发生在 在一个工程里, 有2个target, 一个target用来生成静态framework, 另一个则是用来使用这个静态框架, 这个时候如果是使用不当就会出现上述情况.

解决方法: 将framework所属的类全部拖入framework工程文件夹中, 并在.h和.m选中 framework target, 注意千万不要选择demo target, 一个都不要

在demo中这样使用框架

project -> demo target -> general -> linked frameworks and libraries 加入该静态framework

2. 编译静态framework, 模拟器和真机都要

3. 在代码中导入要使用的头文件如:

#import <HbbLogFramework/Hbb_Logger.h>

然后就可以尽情使用了

5. 真机运行前警告: App installation failed

The application does not have a valid signature.

解决办法:

处理办法是: project -> target -> Hbb_LogDemo(框架工程)  -> general -> embed binary 删除框架引用

6. 所有操作必须使用clean才能使用, 否则将不准确

7. static 的 framework 不能在访问框架内部的资源文件(也就是bundle文件), 所以如果framework中如果需要使用到资源文件,

那么需要同时创建一个bundle文件, 将资源文件放入其中, 使用时: framework和bundle文件一同拉入工程, 才可以使用.

8. framework的代码本身含有category文件, 并且demo外部也引用了含有category文件的库

连坑不断, 唉, 没办法, 谁叫我是coder呢

解决方法:

project -> target -> Hbb_LogDemo(框架工程) -> build settings -> linking -> other linker flags

添加:

-force_load

$(BUILT_PRODUCTS_DIR)/Hbb_ShareFramework.framework/Hbb_ShareFramework

Hbb_ShareDemo/libWeiboSDK/libWeiboSDK.a

注意: 框架应该排在前面, 顺序不对的话, 在真机上同样没效果的,切记!!

$(BUILT_PRODUCTS_DIR)是产品框架生成根目录

8.2 如果同时有多个框架需要配置-force_load

那么应该这么写:

-force_load

Hbb_ShareFramework.framework/Hbb_ShareFramework

ManyFrameworkDemo/libWeiboSDK/libWeiboSDK.a

-force_load

Hbb_LogFramework.framework/Hbb_LogFramework

9.Missing sumodule 'Hbb_LocalDataBaseFramework.HP_FMDBOperator'

解决方案: 所有公开的类 (标识为public), 必须在Hbb_LocalDataBaseFramework.h中导入, 这是这个框架的头文件, 否则就会有这样的警告

10. dyld: Library not loaded:   Reason: image not found

dyld: Library not loaded: @rpath/Hbb_LocationFramework.framework/Hbb_LocationFramework

  Referenced from: /private/var/mobile/Containers/Bundle/Application/44478571-ECDB-4BC7-B440-AECCF28B20C7/Hbb_LocationDemo.app/Hbb_LocationDemo

  Reason: image not found

证明你创建的是动态的framework, 我们要改成静态的

解决方案: project -> framework target -> build settings ->linking -> Mach-O Type 改为 static

默认是dynamic

11. 引用到libxml2.dylib动态库

框架文件本身要加入libxml2.dylib, 并且还要配置search paths ->header search paths 中加入 /usr/include/libxml2

12. 框架的.h文件中声明多个类interface, 而没有@implementation

这个时候需要为这多个interface声明  @implementation, 不能直接写.h文件里, 而是需要创建.m文件实现这多个@interface

如: Hbb_IMComm.h

/**

*  登陆信息

*/

@interface Hbb_IMLoginParam :TIMLoginParam

@end

那么就需要创建一个Hbb_IMComm.m文件, 并且在其中编写如下代码:

@implementation Hbb_IMLoginParam

@end

13.参考自: 真机运行出现 file was built for archive which is not the architecture being linked (armv7s) 报错

真机file was built for archive which is not the architecture being linked (armv7s)

原因是在在真机上生成真机自身architecture, 没有生成别的型号的architecture, 比如我在iphone5s上生成了framework, 放在ipad mini上就运行不了

解决方法:

找到项目的Build Settings- > Build Active Architecture Only,将其从NO 设为 YES

14. 声明为public的头文件, 不需要全部导入到xxx_framework.h文件中

15.

方法一. xib文件必须放在[NSBundle mainBundle](主套装)里,才能被编译运行

方法二. 在framework中打包xib target->build phases->copy bundle resources + (把自己的framework加上去, 那么framework里的的资源都会顺带被存储在main bundle里)

Xcode 6制作动态及静态Framework和各种坑的更多相关文章

  1. Xcode 6制作动态及静态Framework

    技术交流新QQ群:414971585 有没有写SDK或者要将一些常用的工具类做成Framework的经历? 你或许自己写脚本完成了这项工作,相信也有很多的人使用 iOS-Universal-Frame ...

  2. 【转】Xcode 6制作动态及静态Framework

    http://years.im/Home/Article/detail/id/52.html 创建iOS动态库 新建工程并选择默认Target为Cocoa Touch Framework, 如图: 做 ...

  3. xCode6制作动态及静态Framework(转)

    原文:http://years.im/Home/Article/detail/id/52.html 相关推荐:http://www.cocoachina.com/ios/20150127/11022. ...

  4. Xcode6制作动态及静态Framework[repost]

    有没有写SDK或者要将一些常用的工具类做成Framework的经历? 你或许自己写脚本完成了这项工作,相信也有很多的人使用 iOS-Universal-Framework ,随着xCode6的发布,相 ...

  5. Xcode + Swift 制作动态原型

    转载: Xcode + Swift 制作动态原型 为什么是 Xcode 和 Swift 我们尝试过的动态原型设计工具,Origami, Form, Hype, FramerJS,Pixate 等,但都 ...

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

    静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用. 什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人 ...

  7. iOS 使用 Xcode8 制作动态库及静态库

    在使用第三方 SDK 时,经常遇到他们提供的仅仅只有一个动态或静态库,并不能获取源码.使用动态库 FrameWork 或 静态库 Lib,可以满足不想把核心代码的具体实现向使用者展示,又能避免其他人错 ...

  8. iOS 静态库,动态库与 Framework

    iOS 静态库,动态库与 Framework     静态库与动态库的区别 首先来看什么是库,库(Library)说白了就是一段编译好的二进制代码,加上头文件就可以供别人使用. 什么时候我们会用到库呢 ...

  9. 关于VS2013下制作和使用静态库和动态库

    关于VS2013下制作和使用静态库和动态库 引言 什么是库:库是写好的现有的,成熟的,可以复用的代码. 所谓静态.动态是指链接.将一个程序编译成可执行程序的步骤: 静态库在链接阶段,会将汇编生成的目标 ...

随机推荐

  1. Cross-Site Scripting(XSS)简介

    最近才开始研究HTML以及安全问题.如果有什么说得不对的地方,望请指出. 在网络应用安全中,XSS可能是最常见,范围最大,所包含攻击方法最多,同时也是最难以理解的一种攻击.在OWASP所列出的十大网络 ...

  2. matlab连接sql数据库

    最近项目还涉及到matlab连接数据库,下面我就记录如何进行配置使得matlab能够连接sql数据库.由于最近工程做的多一些,所以分享的都在工程配置上,当初为了这些配置可是反复卸载与重装,算法其实也有 ...

  3. 《Struts2.x权威指南》学习笔记2

    在学习了第二章后,我想要将struts分类,修改一下struts.xml的默认读取路径如下图. 在IntelliJ中,resources是struts的默认路径 修改路径,需要在web.xml中添加s ...

  4. 获取网卡的MAC地址原码;目前支持WIN/LINUX系统 获取机器网卡的物理(MAC)地址(服务器端)

    <?php class GetMacAddr{ var $return_array = array(); // 返回带有MAC地址的字串数组 var $mac_addr; function Ge ...

  5. win7下如何建立ftp服务器

    前段时间正在做一个项目,需要上传东西到ftp服务器,纠结于如何建立ftp服务器.经过一番摸索.终于成功建立ftp服务器.现将我的经验跟大家分享一下.不足之处还望多多指点! 步骤/方法 首先在本地机器上 ...

  6. ArcGIS Server开发教程系列(8)ArcGIS API for Javascript-控件(小部件)(续)纯代码

    完整代码如下: <html> <head> <meta http-equiv="Content-Type" content="text/ht ...

  7. Wget命令下载、备份博客

    -np http://www.cnblogs.com/memory4young/p/ 参考资料: http://www.cnblogs.com/memory4young/p/wget-backup-b ...

  8. MinGW: TOO MANY SECTIONS issue

    http://sourceforge.net/p/mingw-w64/mailman/mingw-w64-public/thread/509A38C6.6070807@doxos.eu/ MingGW ...

  9. css 常用代码解析

    .cBan_1 .e2-pro li a{ display: block; -webkit-transition: all 0.3s linear;transition: all 0.3s linea ...

  10. 浅谈T-SQL中的联接查询

    引言 平时开发时,经常会使用数据库进行增删改查,免不了会涉及多表联接.今天就简单的记录下T-SQL下的联接操作. 联接类型及其介绍 在T-SQL中联接操作使用的是JOIN表运算符.联接有三种基本的类型 ...