您的位置:首页 > 其它

制作兼容64位的FrameWork

2015-04-27 16:27 225 查看
最近在要把写的代码打包成Framework包给其他人用,照着网上的博客介绍的过程做出来以后,发现在64位上运行时会崩溃掉,数个小时的google和测试后,最终在github上找到了一位大神写的文章,他自己写了一个脚本,将该问题解决掉了。(32个赞!)

下面是对他文章的翻译,注释是我在制作的工程中发现要注意的地方,原文地址:https://github.com/csexton/ios-framework-builder/blob/master/HOWTO.md

我的灵感极大程度上来自于Jeff VerKoeyen的iOS Framework指南,然而当苹果发布了新的64位架构后,打破了原有的一些事情。主要是我们现在有32位和64位的模拟器架构。不幸地是xcode并没有给你任何的方式来指定你想给模拟器设置的架构,当我在模拟器里编译时,我无法弄清楚如何巧妙地编译一个臃肿的二进制包。我可以用xcodebuild和一些环境变量来解决这个问题。我还希望把所有的框架编译工作放在一起,而不是对不同的target进行不同的编译设置。

在这篇文章的末尾,你将得到一个单独的aggregate target,它会构建你的二进制包,并把它编译成.framework。原始的target通常只是构建了一个.a文件。

1 制作一个通用的Cocoa Touch Static Library

1.1 创建一个新的工程

如果有必要,那就创建一个新的library工程。

这只会生成一个.a二进制包和hearders文件。我们将添加一个target,这个target将把它转换成一个合适的framework



1.2 Framework Header

你需要创建一个这个framework总的要导入的头文件。这个会提供给你的library的使用者,而不是这个library的本身。

例如我已经有一个叫作"MyFramework"的framework包,还有一个MyFramework/MyFramework头,像下面这样:

[objc]
view plaincopy





#import <Foundation/Foundation.h>
#import <MyFramework/MyFramework.h>

我只要将该头文件添加到工程中即可使用MyFramework包中的功能了。

1.3 公开的文件

首先你需要添加一个Copy Header build phase。到状态栏,然后点击:

Editor
Add Build Phase
Add Copy Headers Build Phase

(注:不知道为什么我这里上面的那一项是不可用的,不过选中target后,在Build Phases中下面搜索框前面的+也是可以的,如下图)



这时你看的界面如下所示:



为了暴露你插件包中的功能,你需要公开对外可见的头文件。用XCode的"Target Membership"来完成。

为了设置membership mash ⌘-⌥-0,然后勾选或不勾选Target Membership,然后设置为"project"



注:当你要把一个头文件放到.framework里时,你就要这样做。

(注:在我测试的过程中发现,要把那一项设置为public的才行,是project的话对外还是不可见的)

更新公开头文件的路径。在build setting中搜索"Public Headers Folder Path",然后将其值改变为:

[objc]
view plaincopy





$(PROJECT_NAME)Headers

(注:这一项一定要设置,否则头文件里任何一个.h文件都没有)

这会把头文件放到一个像MyProjectHeader中的文件夹中,它会被复制到framework包中。

1.4 禁用Code Striping

点击工程名,build settings,选择"combined",然后更新下面的设置:

Dead Code Stripping: No
Strip Debug Symbols During Copy: No
Strip Style: Non-Global Symbols

2 Framework Target

创建一个新的aggregate target

从菜单栏中添加一个新的target:

File
New
Target
Other
Aggregate



像Jeff一样,我喜欢称它为"Framework"

Add Dependencies

现在点击project,然后编辑"Build Settings",在这里你可以把你的library当作framework的一个依赖项。



现在你会看到它列在了你的工程下面:



构建Framework

现在添加build phase,它将运行脚本来完成编译和文件创建。
在菜单栏上选择:

Editor
Add Build Phase
Add Run Script Build Phase

现在你有两个选择,你可以粘贴脚本内容到XCode工程设置中,或者只是使用它调用你的脚本。我更喜欢后者,因为git可以追踪并整合你的编译脚本就像正常的代码一样。



只要粘贴一个命令到你的脚本中,如果有必要再调整路径。

[objc]
view plaincopy





set -e
${PROJECT_DIR}/${PROJECT_NAME}/Scripts/framework-builder

这是真正神奇的地方,这个脚本处理了所有关键的事情。把脚本内容放到一个叫Scripts/framework-builder的文件中,然后添加到工程中。
关于这个脚本有几点要解释一下:

我使用的ruby,很大程度是因为它在bash中处理的大量复杂的边界情况。然后这可以使用包括中Mac OS X系统中的ruby命令,根本无需依赖包。它应该会起作用。他设置避免了任何可能被安装的ruby版本的管理。
这会重新编译所有target来确保满足所有包括进来的架构。这是一个有点笨拙的解决方案,但是却很管用。
假定$PROJECT\_NAME与你的library和framework的名字一样。如果不是就编辑一下这个脚本。

从github standalone/framework-builder上可以下载这个脚本,或者执行如下命令:

[ruby]
view plaincopy





curl https://raw.github.com/csexton/ios-framework-builder/master/standalone/framework-builder > Scripts/framework-builder

如果你对这个处理过程有任何更改,请随意发送pull request,我相信苹果会有一天对其进行改善的。

(注:编译成功之后,那生成的framework包在哪里呢?右击XCode左边的Products组下面的.a文件,然后在Finder中显示,打开以后并没有framework包,它实际上在这个文件夹的上一层,好了,复制好路径,到前往中把它找到吧!)

============翻译完毕,如果有任何不对的地方希望留言指出^_^======================

制作资源Bundle包

写到这还没有结束,因为在我制作的framework包中用到了xib、图片这些资源,这些东西是不能一起放到刚才制作的framework包里的,所以还要把他们一起打包出来一个bundle包,在使用时这个bundle包和framework包一起使用。好了,现在说一下这个bundle包的制作过程吧!

1. 再添加一个target,然后选择Bundle那项,如下:



命名为:Resource。这时在Xcode的左边多了下个组,如下:



为了使我们生成的Resource.bundle里只有需要的资源文件,没有其他的多余项,上面的那些可以完全删除掉。

2. 在Targes中选中Resource,到Build Settings中将Base SDK改为Latest iOS(iOS版本)

3. 还是在Build Settings中到Packaging下将Info.plist File后面的值删掉,否则编译的时候会报找不到.plist文件(因为刚刚到删掉掉了- -)

4. 在Build Phases中将涉及到的xib和图片都放到Copy Bundle Resources中。

5. 再选择Targest的Framework,在Target Dependencies中添加Resouce.bundle,这样在编译Framework时,这个bundle包也会一起被编译的。

6. 现在编译还会出错,之前引用到的图片都多了一层目录,所以所有用的地方都要改一下,例如如下:

[objc]
view plaincopy





NSBundle *bundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"MyFrameworkResources" withExtension:@"bundle"]];
MyViewController *controller = [[MyViewController alloc]initWithNibName:@"MyViewController" bundle:bundle];

但是请注意:在IB中对xib上的控件设置的图片并不会收到影响,不用加上这一层路径

7. 选择Framework,然后编译,经过我的测试,选择是模拟器还是Device都是一样的。

8. 找到Framework包和bundle包,一起交给别用去用吧^_^。

有几点要注意的:

Resource.bundle一定要放在使用这个资源包的工程下面,否则会找不到资源文件。
我刚才说的是把写好的代码,打包出来一个framework包和bundle包,你在做时最好是直接建一个cocoa touch static library工程,然后再建一个测试的工程,以免文件过多时再打包不好弄。在测试工程中可以直接引用生成的framework,这样在编译完framework后就不用再替换一次测试工程中的framework包了。
如果在framework中用到了category,那么在使用该framework包时要在工程的Build Settings中Linking下的Other Linker Flags设置为-all_load。
尽量在源代码所在的那个目录中只放.m和.h文件,因为这个目录中的所有文件都会被放到framework包中,这样徒增了framework包的大小。
在新建完Cocoa Touch Static Library后,你会发现在Frameworks的组下面少了UIKit.framework,如果你用到了这里面的东西,那在Build Phases中将其添加进来就好。



参考文章:http://www.galloway.me.uk/tutorials/ios-library-with-resources/

在这篇文章中作者写的两个category,一个是对NSBundle,一个是对UIImage,用起来很方便:

[objc]
view plaincopy





@interface NSBundle (MyLibrary)
+ (NSBundle*)myLibraryResourcesBundle;
@end

@implementation NSBundle (MyLibrary)

+ (NSBundle*)myLibraryResourcesBundle {
static dispatch_once_t onceToken;
static NSBundle *myLibraryResourcesBundle = nil;
dispatch_once(&onceToken, ^{
myLibraryResourcesBundle = [NSBundle bundleWithURL:[[NSBundle mainBundle] URLForResource:@"MyLibraryResources" withExtension:@"bundle"]];
});
return myLibraryResourcesBundle;
}

@end

[objc]
view plaincopy





@implementation UIImage (MyLibrary)
+ (UIImage*)myLibraryImageNamed:(NSString*)name;
@end

@implementation UIImage (MyLibrary)

+ (UIImage*)myLibraryImageNamed:(NSString*)name {
UIImage *imageFromMainBundle = [UIImage imageNamed:name];
if (imageFromMainBundle) {
return imageFromMainBundle;
}

UIImage *imageFromMyLibraryBundle = [UIImage imageWithContentsOfFile:[[[NSBundle myLibraryResourcesBundle] resourcePath] stringByAppendingPathComponent:name]];
return imageFromMyLibraryBundle;
}

@end
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: