iOS 制作兼容64位iOS系统的Framework包

最近在要把写的代码打包成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

iOS 制作兼容64位iOS系统的Framework包_第1张图片

1.2 Framework Header

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

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

#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中下面搜索框前面的+也是可以的,如下图)
iOS 制作兼容64位iOS系统的Framework包_第2张图片

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

iOS 制作兼容64位iOS系统的Framework包_第3张图片

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

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

iOS 制作兼容64位iOS系统的Framework包_第4张图片

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

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

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

$(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
iOS 制作兼容64位iOS系统的Framework包_第5张图片
像Jeff一样,我喜欢称它为"Framework"

Add Dependencies

现在点击project,然后编辑"Build Settings",在这里你可以把你的library当作framework的一个依赖项。
iOS 制作兼容64位iOS系统的Framework包_第6张图片
现在你会看到它列在了你的工程下面:
iOS 制作兼容64位iOS系统的Framework包_第7张图片

构建Framework

现在添加build phase,它将运行脚本来完成编译和文件创建。
在菜单栏上选择:
  • Editor
  • Add Build Phase
  • Add Run Script Build Phase
现在你有两个选择,你可以粘贴脚本内容到XCode工程设置中,或者只是使用它调用你的脚本。我更喜欢后者,因为git可以追踪并整合你的编译脚本就像正常的代码一样。

iOS 制作兼容64位iOS系统的Framework包_第8张图片

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

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上可以下载这个脚本,或者执行如下命令:
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那项,如下:

iOS 制作兼容64位iOS系统的Framework包_第9张图片

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

iOS 制作兼容64位iOS系统的Framework包_第10张图片

为了使我们生成的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. 现在编译还会出错,之前引用到的图片都多了一层目录,所以所有用的地方都要改一下,例如如下:
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中将其添加进来就好。
iOS 制作兼容64位iOS系统的Framework包_第11张图片

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

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

@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

@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


你可能感兴趣的:(ios,framework,64位)