ios学习--XCode标准Framework静态库制作方法 & 工程转Framework静态库加xib和图片的完美解决方案

一、图文介绍Framework库的制作过程

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

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

创建iOS动态库

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

11.png

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

12.png

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

13.png

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

14.png

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

15.png

16.png

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

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

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

运行报错(Reason: Image Not Found)

18.png

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

19.png

注意: 在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

20.png

添加script到新建的Target

21.png


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文件

22.png

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

Xcode 6下制作通用静态库

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

23.png

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

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

24.png

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

25.png

判断一个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 x

二、

标准Framework静态库制作方法。工程转Framework,静态库加xib和图片

1、创建一个工程,或者在原有的工程上进行。不在列举 做法如上。
2、在原有工程上添加一个静态库

 

2.1、选择OS X 的Bundle。因为Xcode6.1中iOS里没有Bundle。

 

2.2、修改Bundle的属性,一是让他能用在iOS上。二是改变Bundle成为Framework
选中要修改的Bundle

 

2.3、Target:并选择 Build Settings ->  Architectures -> Base SDK  改为Latest iOS(ios 8.1)

 

2.4、Target:在 Deployment 下,
将 “Mac OS X Deployment Target”改成”Compiler Default”,
将 “Targeted Device Family”改成”iPhone/iPad”,根据自己的需要改。如果你只想在iPhone上用,那选iPhone就行
将  “iOS Deployment Target”,改成 “iOS 7.0”:也就是说这个静态库支持7.0以上版本。

2016.2.29更新 :
以上的设置方法对于Xcode 7+版本已经不适合。

PS:将 “Mac OS X Deployment Target”设置成 ”可选的最高版本”,

        将 “Targeted Device Family”改成”1,2”

           “iOS Deployment Target“这个选项卡 在原位置找不到了,暂时不知道 [在build Settings -》搜索  “ios ” 可发现此选项卡,不过会出现(null)-Deployer ,暂时设置为最低支持的ios版本,希望有用]。







 

2.5、Target:在 Linking 下,
将 “Dead Code Stripping” 改为 “NO”,
将 “Link with Standard Libraries” 改为 “NO”,
将 “Mac-O Type” 改为 “Relocatable Object File”:

2016.2.29更新 :
以上的设置方法对于Xcode 7+版本已经不适合。Dead Code Stripping、Link with Standard Libraries无需在设置成NO。Mac-O Type建议设置成静态库(Static Library),如果必须使用动态库(Dynamic Library),建议查阅Apple 开发文档。
另外我发现一篇介绍IOS库的文章,非常好,很实用,我粘贴了过来,有兴趣朋友可以看下。
http://www.tuicool.com/articles/VFFjmq6


 


2.6、Target:Packaging 中,
将 “Wrapper Extention” 改为“framework”:


 

2.6、Target:返回Info 标签。
将 “Bundle OS Type Code” 改为 “FMWK”(Framework )


 

2.7、Target:返回Build Phases 标签。加入Copy Headers,后边会用到。

  
 

3、返回目录,可以看到设置好的 Framewor文件。红色代表还没有这个静态库,需要你生成一下。后边会讲到。


 

3.1、如何管理静态库:

 

点开菜单,可以看到所有可管理的工程、静态库。
3.2:LineChart(App图标样式)是App 工程文件,选中这个运行(command+R 或command+B)意思是让工程导出或者测试运行可发布的应用程序(就是上线文件)。对应功能就不一一介绍了。
3.3:PZGLineChart(Bundle图标样式)是静态库。选中这个运行(只能运行command+B,不能运行command+R)意思是导出静态库,也就是自己的封装代码,用于共享给其他人。如果选中iOS Device运行(command+R),将会导出真机版本的静态库,如果选中iOS Simulator以下其他任何版本运行(command+R),将会导出模拟器的静态库版本。至于为什么这样,是因为真机的iOS和Mac系统中的虚拟iOS目录结构是不一样的,如果导错了,编译器会报错,“找不到指定文件。”

3.4:选中:Edit Scheme…:在这里管理所有工程文件、静态库、测试、发布等等的程序关系。
在这里,我们把刚才加入Framewor的导入,以便程序直接建立关系。


 
成为这样:

 



3.5:选中:Manage Scheme…:、这里的添加删除、会改变 
 。主要是方便开发者测试使用。
如果希望测试菜单中测试其他工程、静态库等等东西,在这里添加就行,比如下边的这个。Show对勾都勾选上后,菜单就会出来这两个选项。去掉对勾会隐藏。


 


4、给静态库添加文件、xib、image....
直接介绍其属性:
1:不知道。没用过。等高手补充
2:程序文件放的地方:(*.m 、*.mm等),需要压缩保护的代码,只要放在这里的文件,都将被静态库压缩。使用静态库的一方是无法看到源代码的。
3:库文件放的地方,比如把其他的静态库、动态库压缩到这里。
4:资源文件放的地方,图片、音视频、xib、Images.xcassets、甚至是Main.storyboard(故事板);只要放到这里,都将被压缩,使用方是无法编译或者看到源代的。
5:对外接口。主要就是这里,展开后,会看到第一行(Public),不用介绍了吧,看英文应该就能猜到,何况这个单词是程序中常用的单词,公用吗!,只要是放到这里的文件,都将会暴露给使用方,建议把所有的*.h文件放到这里。

现在就把你想要管理的文件一个一个往里拖拽吧。就不在一个一个介绍。

 

6、首先测试,程序部分是否运行正常。我新增加了一个MyViewController(带.xib),在MyViewController.m里简单写了个打印。


  
 

还有我拖拽完就是这个样子:

 
7:生成静态库:大家看到了,都是红色,意思是Xcode找不到文件,别慌,我们都给他跑一边。

 
7.1、选中以下,分别command+B。

  
 

黑了吧, 
 


选中PZGLineChart.framewor文件,鼠标右键,Show in Finder。
看到了吧,成功了,就这么简单。现在生成的是真机版本,如果需要模拟机使用的静态库,选中模拟后,再分别command+B一下。


 



模拟机版本:
模拟机版本的就选择:Debug-iphonesimuator文件夹下。如果没有Debug-iphonesimuator文件夹,那你选择 
 后再command+B。





8、关于静态库引用文件
如果希望你的工程能在未来能导出成静态库,那么在你编写的时候要遵循静态库引用原则,使用这种方式。
注意:这种引用方式必须在你的Products下静态库成黑色时候,才能编译通过。


 



9、新建一个工程,把你做的静态库Add进来。对接好,然后直接Command+R。是不是成功了。其实就这么简单。
关于找不到文件的报错:直接看下边。我有介绍,


 

跑成功的工程,一切正常。



10、关于第三方库找不到文件的提示错误。简单补充下吧,别走弯路。
首先你要确定这个错误产生的原因:大致分两种,
一:你做的项目缺失文件,百度下,加入就解决了;
二:你引入的第三方框架发生找不到文件错误:这样的结果分两种:
二、一:开发第三方框架的程序人员没有做好自己的框架。尤其是在生成框架的时候,没有设置好自身属性。导致使用者用的时候出现找不到文件错误,最后迫使使用者不得不去改自己的工程配置。
二、二:你在引入第三方框架的时候,少引用了文件。

好了,你分析完后就好办了,我们做的这个静态库没有设置自己的兼容属性,所以就照成了这种错误。在iPad Retina的模拟器上跑就正常,换成其他的模拟设备就报错。所以改下其属性就能解决。
Target: -> Build Settings ->  Architectures -> Build Active Architecture Only 全改成NO;

关于制作静态库兼容多版本的设置。

 




分析原因:

  
 
在我们生成静态库的时候,我们选择的是iPad Retina。也就是说这个静态库在引用的时候,必须也是iPad Retina。否则Xcode编译器会找不到文件所在。
大家可以实验下,在iPad Retina下导出静态库,在其他工程使用的时候,如果用iPad Retina跑就没有错误。如果选择其他设备就会提示找不到文件。

兼容全部设备解决方法:改下静态库的兼容属性。Target: -> Build Settings ->  Architectures -> Build Active Architecture Only 全改成NO;
Build Active Architecture Only
这个属性设置为yes,是为了debug的时候编译速度更快,它只编译当前的architecture版本,所以会报错编译不到文件,出错("_OBJC_CLASS_$_xxxxxx", referenced from:)       
而设置为no时,会编译所有的版本,这样就解决编译出错的问题了。      
这个是设备对应的architecture:
armv6:iPhone 2G/3G,iPod 1G/2G
armv7:iPhone 3GS/4/4s,iPod 3G/4G,iPad 1G/2G/3G
armv7s:iPhone5, iPod5
arm64:iPhone5s
编译出的版本是向下兼容的,比如你设置此值为yes,用iphone4编译出来的是armv7版本的,iphone5也可以运行,但是armv6的设备就不能运行。




真机版和模拟器版的库合并解决方法:在framework文件夹下,你会看到一个白板文件,这个文件名和的的framework静态库名相同,只是没有后缀名。 这个文件在被引入到其他工程时候是看不见的。只有在Show In Finder下能看到。
打开终端,输入命令:中文换成你的真实目录。

lipo -create “……真机/目录/那个白板文件“ "……/模拟器/目录/那个白板文件" -output “…..另保存的/目录/文件”

合并好的新文件,覆盖掉原来的framework中的文件即可。这个framework就会支持所有设备和真机、模拟器全部版本。
其实这个方法就是用*.a文件的合并方法。在framework一样有效。framework中的白板文件就好像*.a文件一样。



9、关于xib的引用。同样根上边创建framework的方法一样,只是设置不同。我加了一个名字为:Resource.bundle静态资源文件。
这个样子:

 


Target:并选择 Build Settings ->  Architectures -> Base SDK  改为Latest iOS(ios 8.1)
Target:在 Deployment 下,
Target:将 “Mac OS X Deployment Target”改为”Compiler Default”,
Target:将 “Targeted Device Family”改”iPhone/iPad”,
Target:将 “iOS Deployment Target”,改为 “iOS 7.0”:


9.1、在MyViewController.m文件中编写下。别忘了在*.h里写上接口。

 


9.2、然后就是给新加的资源文件拖拽下。

 

再选中工程文件,拖拽后的样子(就是转静态库的工程)。
为了方便查看,我把MyViewController.xib背景涂成了蓝色,加了个红按钮。直接:command+R。一切运行正常。




9.3、然后把这两个文件都复制出来。 
 

放到其他工程里,运行。是不是很简单。




10、加个图片。怎么加不解释来。先加一个不用管理器管理的图片。
工程command+R。。一切正常。


 


10.1、其他工程引用静态库,再跑。正常。嘿嘿,是不是多了个图片文件。



10.2、提高难度,我们在Images.xcassets图片管理器里加图片。我加了个矩阵图片,并设置了矩阵属性。

 



在xib里加个按钮。全设备匹配按钮。我没有写一行代码,只是在xib里拉个按钮。然后设置了下背景。系统自动识别图片。关于这个大家可以度娘下。

 


不管三七二十一,直接把Images.xcassets拉到资源库。

 



工程跑,OK。没问题。




引入静态库。OK。没问题。有没有发现。多了一个不明的文件:Assets.car。这个就是Images.xcassets管理,里边所有的图片都被封装了,也就是说被Images.xcassets管理的图片竟然也被加密保护了起来。疯狂吧。狂叫吧。呵呵,“码农”一族们。快转变思维吧,不然很快被Apple淘汰了。





感谢:http://www.cocoachina.com/bbs/read.php?tid-282490.html

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





--------------------------------------------------------------------------------------------------------------------------------------------------------------------

PS:

/*

 第一步:创建静态库项目

 新建项目->IOS->Framework & Library->Cocoa Touch Static Library

 

 第二步:添加需要复制出去的文件

 简单来说,要把需要引用的类的.H文件复制到一个公共的目录,便于其它项目访问.

 在这里要说明下,原作者创建静态库似乎默认就有个MyViewController,但我新建时是没有的,所以需要手动新建一个测试用的类.建议使用UIViewController

 选择MyLibrary项目,File->New->New File->Cocoa Touch->UIViewController subclass,名称就叫MyViewController

 再选择MyLibrary项目,然后在右边面板选择Build Phases,点下方Add  Build Phases,再点Add Copy Files

 Destination选择Products Directory,subpath输入include

 

 第三步:添加资源Bundle Target

 资源必须是单独编译成Bundle才能使用,所以需要为MyLibrary项目生成另一个Bundle Target

 选择MyLibrary项目,在右边面板中点Add Target->MAC OS X->FRAMEWORK &LIBRARY->Bundle

 需要说明,bundle项目只有MAC OS X里才有,但实际上也是可以用于IOS,只是需要做些改动

 

 第四步:修改资源bundle target

 bundle target修改成ios能用的,看图

 Build Settings设置

 1)ArchitecturesStandard (armv7)

 2)Build Active Architecture Only 为指定的IOS版本

 

 第五步:添加 XIB的输出

 设置XIB为输出到BUNDLE,这一步相当于VS c#开发里把某个图片设置编译动作为嵌入资源.

 选择Mylibrary项目,targets选择MyLibraryResources,Build Phases面板

 Copy Bundle Resources+选择xib文件

 

 6:创建测试程序

 现在创建一个测试程序来看看调用静态库里的MyViewController效果

 

 7:关联静态库引用

 编辑Scheme(菜单Product->Edit Scheme)

 选择Build

 +,选择MyLibraryMylibraryResources项目

 

 第八步:其实还是关联静态库引用

 上一步是做了编译主程序自动编译静态库,这一步是要把静态库生成的.a文件动态引用到主程序里

 选择MylibraryResources项目在右边Build Phases面板Link Binary WithLibraries+,选择libMylibrary.a文件

 现在需要把静态库中的bundle也与主程序做关联

 还是在Build Phases面板

 展开Copy Bundle Resources

 要注意,这次不是点+,需要拖拽Mylibrary项目中的xib文件到Copy Bundle Resources

 

 第九步:最后设置主程序项目

 选择MyLibraryResource项目在Build Settings面板里

 设置User Header Search Paths$(BUILT_PRODUCTS_DIR),注意建议把Recursive勾上(递归搜索)

 

 10:编写MyviewController代码

 - (id)init {

 NSBundle *bundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"MyLibraryResources" withExtension:@"bundle"]];

 if ((self = [super initWithNibName:@"MyViewController" bundle:bundle])) {

 }

 return self;

 }

 

 */

















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值