workspace & subProject & target

http://blog.itpub.net/12231606/viewspace-1079867/

最近新入一个项目组,工程checkout下来以后久久编译报错,各种reference引用错误。甚是头痛,看了半天的工程,发现他们的工程结构有些杂乱,大量的用了workspace ,subProject以及target。原来的项目框架很简单顶多引入一个target,实在对各种环境的配置不甚了解。于是忽猛查资料,下边就是搜索资料的一些汇总。

一:Xcode 环境简介

首先弄清楚一些在XCode环境下的一些概念:

Workspace:

简单来说,Workspace就是一个容器,在该容器中可以存放多个你创建的Xcode Project, 以及其他的项目中需要使用到的文件。

使用Workspace的好处有:

1.扩展项目的可视域,即可以在多个项目之间跳转,重构,一个项目可以使用另一个项目的输 出。Workspace会负责各个Project之间提供各种相互依赖的关系;

2.多个项目之间共享Build目录。

Project:

指一个项目,该项目会负责管理生成一个或者多个软件产品的全部文件和配置,一个Project可以包含多个Target。

Target:

一个Target是指在一个Project中构建的一个产品,它包含了构建该产品的所有文件,以及如何构建该产品的配置。

Scheme:

一个定义好构建过程的Target成为一个Scheme。可在Scheme中定义的Target的构建过程有:Build/Run/Test/Profile/Analyze/Archive

BuildSetting:

配置产品的Build设置,比方说,使用哪个Architectures?使用哪个版本的SDK?。在Xcode Project中,有Project级别的Build Setting,也有Target级别的Build Setting。Build一个产品时一定是针对某个Target的,因此,XCode中总是优先选择Target的Build Setting,如果Target没有配置,则会使用Project的Build Setting。

关于如何构建以上环境,请参考devCenter官方指南:

https://developer.apple.com/library/ios/recipes/xcode_help-structure_navigator/articles/Adding_an_Existing_Project_to_a_Workspace.html

二:Xcode 创建workspace多工程依赖

新建 Xcode workspace

打开 Xcode , 选择 File -> New -> Workspace , 将 Workspace 命名为 Test.xcworkspace , 并选择合适的目录。

三:Xcode 创建子工程以及工程依赖

背景:由于创建一个app的时候需要引入大量的依赖三方库或者自己写的工具类,每次新建app这些事情都需要大量的时间。所以,就考虑建一个公共工程,每次新建项目,只是需要依赖这个工程就行了。

思路设计:①BaseApp,用于存放公共的库。②BaseAppSample用于写demo和示例,方面别人在使用的时候查看和借鉴。

具体步骤:

1、创建BaseApp工程,注意这里被依赖的工程要是Static Library。

2、创建BaseAppSample工程。在BaseAppSample的framework group上面右键,添加文件,选中BaseApp.xcodeproj,建立依赖关系。

行成如上图所示的依赖关系。
3、这样BaseAppSample里面就可以使用BaseApp里面的类了么?开始我以为可以了,可是我错了。在BaseAppSample里面import相关的类会出现找不到的error,会编译错误。需要进行下面的操作: 在Header Search Paths 里面添加引入头文件的路径,如上所示,这样就不会出现编译的error了。
4、等等,好像以为正确,comand + r。Oh,NO,还是有错误。还是编译还出现找不到相关的文件。
这个时候可以判断的是,编译的结果没有能够正确的依赖,进行下面的步骤
添加target的依赖关系,保证编译BaseAppSample的时候先编译BaseApp。
选中BaseAppSample Target,然后选中其Build phases选项,添加BaseApp target 然后把lib也引入依赖中,如下所示: 5、如果被依赖包(BaseApp)中有图片或者依赖文件怎么办?
要把相关的依赖图片copy出去:选中BaseApp target,进入Build phases选项,找到最下面的copy file栏,添加相关的文件:

到这一步,只要command+r,BaseAppSample中就能够正常地引入BaseApp里面的功能了。

以后有公共的组件,只是需要添加到BaseApp中,其他依赖的功能都能够使用了。

为了维护方便,可以把BaseApp用单独的SVN或者Git来维护。

分割线追加:

————————————————————————————————————————

今天右发现新的问题,如果存在category的时候,使用category会导致Crash

解决办法是在Sample里面添加编译的命令:

在Other Linker Flags添加 -Objc和-all_load选项,保证category能够被正常的引入。

原文地址:http://www.ganlvji.com/?p=128

四:在Xcode中添加多个targets

(转载地址: http://blog.csdn.net/ysysbaobei/article/details/10951991

在ios开发时,我们经常会遇到对同一个app开发多个版本(Pro、Lite、Free)的情况,这里就涉及到xcode里通过添加多个targets来进行版本控制的问题了,下面就简单说明一下:

点击左侧的工程名称,右侧会出现PROJECT和TARGETS,点击你现在的target,假如叫A,右键弹出菜单中,选择Duplicate,复制一个相同的target,复制的target一般叫A copy,A copy和A的设置(编译条件、源文件、资源文件)完全一样,此时你可以根据需要修改A copy的编译条件和资源文件了;

1、A copy的名称是不是显得不够专业?现在来修改一下:

1)首先修改Xcode左上角的target名称:

点击xcode左上角Run、Stop右边的工程名称,下拉框中选择Manage Schemes,在弹出框中,点击A copy那行,点击一次、再点击一次,就可以修改A copy为你想要的名字了,比如ALite;

2)修改xcode左下方Products下的A copy.app名称:

点击Targets下你刚才改名后的ALite,点击Bulid Setting,搜索Packaging下的Private Headers Folder Path,修改A copy.app/PrivateHeaders为:ALite.app/PrivateHeaders;

2、修改Bundle Identifier和选择不同的证书,让app区分开来

1)新target需要的.plist文件

新建一个文件夹X,添加X到项目中,添加时选择target为ALite,不要选target A了,因为不是共用的;复制原来target的A-Info.plist到X,修改其名称为ALite-Info.plist,xcode中右键Add File To …,选择添加到target ALite中;

2)点击target: ALite ->Summary:提示你需要选择plist文件,选择1)中添加的ALite-Info.plist;

3)点击target: ALite ->Summary:设置Bundle Idenfitier;

4)点击target: ALite->Build Settings->Code Signing:选择另外的证书;

3、修改程序名称

一般说来,多个target的程序名称不同,复制zh-Hans.lproj和en.lproj下的InfoPlist.strings文件到X,xcode中右键Add File To …,选择添加到target ALite中;然后修改InfoPlist.strings的内容:CFBundleDisplayName=”程序名称Lite”;

4、2个target到现在就创建好了,你添加资源文件的时候,通过选择添加的target来控制不同版本的内容;再说一下预编译宏的事情:target->Build Setting,搜索:Preprocessor Macros,设置Debug和Release里的预编译宏内容,比如TARGET_VERSION_LITE=1表示lite版本(注意=前后不能右空格,有空格会编译不过),程序中对不同版本这样判断:

if TARGET_VERSION_LITE ==1

elif TARGET_VERSION_LITE ==2

endif

参考文章:

1、http://blog.163.com/lengfeng_04/blog/static/80470603201273111549529/

XCode 同一Project创建多个target ;

由于项目需求,同一项目需要发两个版本,两个版本只有小部分不同,特地研究了一下target,小有了解,把创建target的经过记录在案,方便过后查看,如有疏漏,请网友指正。

创建target有两种方式,

1.是通过新建target可以通过File–>New–>Target,然后选择其中一个模板来创建,app类型的target,可以选择Empty Application模板,不过新建的target有自己的AppDelegate和main,这点还没研究好,如何与原来的AppDelegate和main复用

2.另一种方法是通过对原有的target做duplicate,复制一份,目前只研究了如何通过duplicate来新建target,做下记录。

假设原来的target名字为A,我们需要新建一个target B

  1. 在原来的target上右键,选择duplicate,Xcode会复制一个名为A copy的target对象,同时生成一个A copy-info.plist和A copy的scheme

    a)对A copy target改名,可以直接单击target来修改,改成B

    b)A copy-info.plist,默认生成在程序环境根目录,也就是A.xcodeproj的同级目录中,如果想放到里层(比如与A-info.plist放在同级目录),可以先在Xcode删除A copy-info.plist索引,然后拷贝文件到制定目录中,然后更名为B-info.plist,在add到project中。在Project的Build Settings中,修改Info.plist File选项为B-info.plist的目录(相对路径),这样就可以看到Info页了(就是B-info.plist),接着修改ProductName和Bundle identifier,使之成为另一个app。Prefix Header的路径,视具体需求而定是否要修改,如果两个target可以公用同一个Prefix Header,那么就不需要修改这里的路径

    c)修改scheme,在调试的Stop按钮边上,我们可以选择本工程中所有的target来做编译,如果不修改,在这里选择出来的名字就是A copy,为了与新建的target统一起来,同样也要修改这里的名字。点击scheme选择区,然后选Manager Scheme,找到A copy,然后改成你需要的名字,比如B

    用duplicate的好处是,如果两个target的相同点很多,用duplicate,就可以把相关的设置全部拷贝过来,而不需要做过多的修改

    生成一个新的target,一定会与原target有区别,这里可以定义预编译宏,来区分两个版本的不同代码,预编译宏可以在Build Settings中Preprocessor Macros定义,比如在我们新建的target B中定义预编译宏MACRO,然后在代码中通过

if defined (MACRO)

//target B需要执行的代码

else

//target A需要执行的代码

endif

其他:Build Phases(各target编译所包含的内容,需要注意的是,如果创建了target B后,再往A里面添加资源或文件,target B中不会自动增加这些资源,需要手动添加)

1.Compile Sources

需要编译的代码文件

2.Link Binary With Libraries

编译所依赖的库

3.Copy Bundle Resources

编译需要的资源

每个target可以根据具体需要增减里面的内容

2、http://kan.weibo.com/con/3550176548481250?_from=text

http://stackoverflow.com/questions/1807377/xcode-multiple-targets-multiple-internationalized-names

同一份代码多个Target ,在支持多语言下分别设置其名称的方法

真不好用一个标题来概括这个东西。Xcode 4.2+ 在项目多语言包 xx.lproj 里引入了一个叫 InfoPlist.strings 的文件,可以对同一个 App 在不同系统语言下显示不同的 Display Name。比如:

InfoPlist.strings (English) -

“CFBundleDisplayName” = “English Name”;

InfoPlist.strings (Chinese) – “CFBundleDisplayName” = “中文”;

在单 Target 下很容易做,多 Target 的时候就需要做一点额外的处理。在项目目录下新建与 Target 同名的文件夹(同名是为了方便区分),然后将 xx.lproj 文件夹 复制 到各个 Target 下面,目录结构会是这个样子:

./Target1/

en.lproj/InfoPlist.strings

zh-Hans.lproj/InfoPlist.strings

./Target2/

en.lproj/InfoPlist.strings

zh-Hans.lproj/InfoPlist.strings

复制后保持项目目录下还有 xx.lproj 文件夹,里面保留 Localizable.strings,因为多语言化一般是通用的,没必要针对每一个 Target 做多语言。复制后的 Target1/xx.lproj 下只有 InfoPlist.strings。然后添加到 Xcode 项目里,打开 Xcode – Views – Utilities (Command+Option+0),在 Target Membership 下针对不同的 Target 把对应文件夹下的 InfoPlist.strings 对应连接起来,Done。

新建 Static Library 项目

五:新建 Static Library 项目

选择 File -> New -> Project , 项目模板选择 Cocoa Touch Static Library , 项目名称命名为 MyLib.xcodeproj , 注意选中 Use Automatic Reference Counting 。

Xcode 会在项目中自动生成 MyLib.h 和 MyLib.m 文件, 单击 MyLib.h 文件, 添加下面的两个方法定义:

- (NSInteger) add:(NSInteger)a and:(NSInteger)b;
+ (NSString*) connect:(NSString*)str1 and:(NSString*)str2;
再打开 MyLib.m 文件, 添加刚刚定义两个文件的实现:
- (NSInteger) add:(NSInteger)a and:(NSInteger)b { return a + b;}+ (NSString*) connect:(NSString *)str1 and:(NSString *)str2 { return [NSString stringWithFormat:@"%@ %@", str1, str2];}

现在, 最终的文件看起来是这样的:

//
// MyLib.h
// MyLib
//
// Created by gdeic on 4/16/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import
@interface MyLib : NSObject
- (NSInteger) add:(NSInteger)a and:(NSInteger)b;
+ (NSString*) connect:(NSString*)str1 and:(NSString*)str2;
@end
//
// MyLib.m
// MyLib
//
// Created by gdeic on 4/16/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import “MyLib.h”
@implementation MyLib
- (NSInteger) add:(NSInteger)a and:(NSInteger)b {
return a + b;
}
+ (NSString*) connect:(NSString *)str1 and:(NSString *)str2 {
return [NSString stringWithFormat:@"%@ %@", str1, str2];
}
@end

选中 MyLib 项目, 在中间的编辑器窗口中选择项目的 Target , 选择 Build Phases 标签, 展开 Copy Headers 分组, 下面有三个子分组, 分别是 Public 、 Project 与 Private , 将 MyLib.h 拖拽到 Public 分组即可。

保存所有文件, 选择 Product -> Build , 进行编译, 生成 libMyLib.a 文件, 同时也会将 MyLib.h 文件复制到输出目录。

使用静态类库项目

选择 File -> New -> Project , 项目模板选择 iOS -> Application -> Single View Application , 项目名称命名为 MyApp , 注意勾选 Use Storyboards 和 Use Automatic Reference Counting 。

建好项目之后, 项目窗口如下如所示:

将 MyLib 项目拖拽到 MyApp 项目的 Frameworks 文件夹, 在弹出的对话框中选择 Create groups for any added folders , 然后点击 Finish 按钮。

选中 MyApp 项目, 在选择项目的目标 (Target) , 选中 Summary 标签页下找到 Linked Frameworks and Library 分组选项, 如下图:

点击下面的加号按钮, 将工作区的 libMyLib.a 添加进去。

接下来添加头文件搜索目录, 选中 Targets 上面的 Project , 选择 Build Settings 标签页,在搜索框内输入 header search 进行过滤, 找到 Header Search Paths , 添加一行, 输入 ../MyLib , 并选中递归复选框。

现在要先验证一下对 MyLib 的引用是否正确, 打开 MyApp 项目的 ViewController.m , 添加对 MyLib.h 的引用, 如下图所示, 并编译 MyApp , 如果编译成功, 则表示引用正确。

打开 MainStoryboard.storyboard 文件, 在生成的 ViewController 上添加两个 UITextField 、 两个 UIButton 以及一个 UILabel, 如下图所示:

并添加相应的 outlet 和 action , ViewController.h 如下:

//
// ViewController.h
// MyApp
//
// Created by gdeic on 4/19/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *textField1;
@property (weak, nonatomic) IBOutlet UITextField *textField2;
@property (weak, nonatomic) IBOutlet UILabel *resultLabel;
- (IBAction)addButtonClick:(id)sender;
- (IBAction)connectButtonClick:(id)sender;
@end
打开 ViewController.m 文件, 实现 addButtonClick: 和 connectButtonClick: 方法, 在 addButtonClick: 方法中调用 MyLib 的实例方法 add:and: , 在 connectButtonClick: 方法中调用 MyLib 的静态方法 connect:and: , 如下所示: - (IBAction)addButtonClick:(id)sender {
// 获取用户输入的两个数字
NSInteger num1 = [self.textField1.text integerValue];
NSInteger num2 = [self.textField2.text integerValue];
// 初始化一个新的 MyLib 实例
MyLib* myLib = [[MyLib alloc] init];
// 调用实例方法相加
NSInteger result = [myLib add:num1 and:num2];
// 显示结果
self.resultLabel.text = [NSString stringWithFormat:@"%d + %d = %d", num1, num2,result];
}
- (IBAction)connectButtonClick:(id)sender {
// 获取用户输入的两个字符串
NSString* str1 = self.textField1.text;
NSString* str2 = self.textField2.text;
// 调用 MyLib 的静态方法连两个字符串
NSString* result = [MyLib connect:str1 and:str2];
// 显示结果
self.resultLabel.text = result;
}

点击添加按钮时, 效果如下图所示:

点击 Connect 按钮时, 效果如下图所示:

六:一个静态库框架模板: iOS Universal Framework

项目地址: https://github.com/kstenerud/iOS-Universal-Framework

这是一个Xcode 4使用的项目模板,而不是一个新的开源框架。

这是项目主页,介绍得很清楚,我把它抓过来翻译了一下。

我们为什么需要框架(Framework)?

要想用一种开发者友好的方式共享库是很麻烦的。你不仅仅需要包含库本身,还要加入所有的头文件,资源等等。

苹果解决这个问题的方式是框架(framework)。基本上,这是含有固定结构并包含了引用该库时所必需的所有东西的文件夹。不幸的是,iOS禁止所有的动态库。同时,苹果也从Xcode中移除了创建静态iOS框架的功能。

Xcode仍然可以支持创建框架的功能,重启这个功能,我们需要对Xcode做一些小小的改动。

把代码封装在静态框架是被app store所允许的。尽管形式不同,本质上它仍然是一种静态库。

框架(Framework)的类别

大部分框架都是动态链接库的形式。因为只有苹果才能在iOS设备上安装动态库,所以我们无法创建这种类型的框架。

静态链接库和动态库一样,只不过它是在编译时链接二进制代码,因此使用静态库不会有动态库那样的问题(即除了苹果谁也不能在iOS上使用动态库)。

“伪”框架是通过破解Xcode的目标Bundle(使用某些脚本)来实现的。它在表面上以及使用时跟静态框架并无区别。“伪”框架项目的功能几乎和真实的框架项目没有区别(不是全部)。

“嵌入”框架是静态框架的一个包装,以便Xcode能获取框架内的资源(图片、plist、nib等)。

本次发布包括了创建静态框架和“伪”框架的模板,以及二者的“嵌入”框架。

用哪一种模板?

本次发布有两个模板,每个模板都有“强”“弱”两个类别。你可以选择最适合一种(或者两种都安装上)。

最大的不同是Xcode不能创建“真”框架,除非你安装静态框架文件xcspec在Xcode中。这真是一个遗憾(这个文件是给项目使用的,而不是框架要用的)。

简单说,你可以这样决定用哪一种模板:

  • 如果你不想修改Xcode,那么请使用“伪”框架版本
  • 如果你只是想共享二进制(不是项目),两种都可以
  • 如果你想把框架共享给不想修改Xcode的开发者,使用“伪”框架版本
  • 如果你想把框架共享给修改过Xcode的开发者,使用“真”框架版本
  • 如果你想把框架项目作为另一个项目的依赖(通过workspace或者子项目的方式),请使用“真”框架(或者“伪”框架,使用-framework——见后)
  • 如果你想在你的框架项目中加入其他静态库/框架,并把它们也链接到最终结果以便不需要单独添加到用户项目中,使用“伪”框架

“伪”框架

“伪”框架是破解的“reloacatable object file”(可重定位格式的目标文件, 保存着代码和数据,适合于和其他的目标文件连接到一起,用来创建一个可执行目标文件或者是一个可共享目标文件),它可以让Xcode编译出类似框架的东西——其实也是一个bundle。

“伪框架”模板把整个过程分为几个步骤,用某些脚本去产生一个真正的静态框架(基于静态库而不是reloacatable object file)。而且,框架项目还是把它定义为wrapper.cfbundle类型,一种Xcode中的“二等公民”。

因此它跟“真”静态框架一样可以正常工作,但当存在依赖关系时就有麻烦了。

依赖问题

如果不使用依赖,只是创建普通的项目是没有任何问题的。但是如果使用了项目依赖(比如在workspace中),Xcode就悲剧了。当你点击“Link Binary With Libraries”下方的’+’按钮时,“伪框架”无法显示在列表中。你可以从你的“伪”框架项目的Products下面将它手动拖入,但当你编辑你的主项目时,会出现警告:

warning: skipping file ‘/somewhere/MyFramework.framework’ (unexpectedfile type ‘wrapper.cfbundle’ in Frameworks & Libraries build phase)

并伴随“伪”框架中的链接错误。

幸运的是,有个办法来解决它。你可以在”Other Linker Flags”中用”-framwork”开关手动告诉linker去使用你的框架进行链接:

-framework MyFramework

警告仍然存在,但起码能正确链接了。

添加其他的库/框架

如果你加入其他静态(不是动态)库/框架到你的“伪”框架项目中,它们将“链接”进你最终的二进制框架文件中。在“真”框架项目中,它们是纯引用,而不是链接。

你可以在项目中仅仅包含头文件而不是静态库/框架本身的方式避免这种情况(以便编译通过)。

“真”框架

“真”框架各个方面都符合“真”的标准。它是真正的静态框架,正如使用苹果在从Xcode中去除的那个功能所创建的一样。

为了能创建真正的静态框架项目,你必需在Xcode中安装一个xcspec文件。

如果你发布一个“真”框架项目(而不是编译),希望去编译这个框架的人必需也安装xcspec文件(使用本次发布的安装脚本),以便Xcode能理解目标类型。

注意:如果你正在发布完全编译的框架,而不是框架项目,最终用户并不需要安装任何东西。

我已经提交一个报告给苹果,希望他们在Xcode中更新这个文件,但那需要一点时间.OpenRadarlink here

加其他静态库/框架

如果你加入其他静态(不是动态)库/框架到你的“真”框架项目,它们只会被引用,而不会象“伪”框架一样被链接到最终的二进制文件中。

从早期版本升级

如果你是从Mk6或者更早的版本升级,同时使用“真”静态框架,并且使用Xcode4.2.1以前的版本,请运行uninstall_legacy.sh以卸载早期用于Xcode的所有修正。然后再运行install.sh,重启Xcode。如果你使用Xcode4.3以后,只需要运行install.sh并重启Xcode。

安装

分别运行Real Framework目录或Fake Framework目录下的install.sh脚本进行安装(或者两个你都运行)。

重启Xcode,你将在新项目向导的Framework&Library下看到StaticiOS Framework(或者Fake Static iOS Framework)。

卸载请运行unistall.sh脚本并重启Xcode。

创建一个iOS框架项目

  1. 创建新项目。
  2. 项目类型选择Framework&Library下的Static iOS Framework(或者Fake Static iOS Framework)。
  3. 选择“包含单元测试”(可选的)。
  4. 在target中加入类、资源等。
  5. 凡是其他项目要使用的头文件,必需声明为public。进入target的Build Phases页,展开Copy Headers项,把需要public的头文件从Project或Private部分拖拽到Public部分。

编译你的 iOS 框架

  1. 选择指定target的scheme
  2. 修改scheme的Run配置(可选)。Run配置默认使用Debug,但在准备部署的时候你可能想使用Release。
  3. 编译框架(无论目标为iOS device和Simulator都会编译出相同的二进制,因此选谁都无所谓了)。
  4. 从Products下选中你的framework,“show in Finder”。

在build目录下有两个文件夹:(yourframework).framework and (your framework).embeddedframework.

如果你的框架只有代码,没有资源(比如图片、脚本、xib、coredata的momd文件等),你可以把(yourframework).framework 分发给你的用户就行了。如果还包含有资源,你必需分发(your framework).embeddedframework给你的用户。

为什么需要embedded framework?因为Xcode不会查找静态框架中的资源,如果你分发(your framework).framework, 则框架中的所有资源都不会显示,也不可用。

一个embedded framework只是一个framework之外的附加的包,包括了这个框架的所有资源的符号链接。这样做的目的是让Xcode能够找到这些资源。

使用iOS 框架

iOS框架和常规的Mac OS动态框架差不多,只是它是静态链接的而已。

在你的项目中使用一个框架,只需把它拖仅你的项目中。在包含头文件时,记住使用尖括号而不是双引号括住框架名称。例如,对于框架MyFramework:

import

使用问题

Headers Not Found

如果Xcode找不到框架的头文件,你可能是忘记将它们声明为public了。参考“创建一个iOS框架项目”第5步。

No Such Product Type

如果你没有安装iOS Universal Framework在Xcode,并企图编译一个universal框架项目(对于“真”框架,不是“假”框架),这会导致下列错误:

target specifies product type ‘com.apple.product-type.framework.static’,but there’s no such product type for the ‘iphonesimulator’ platform

为了编译“真”iOS静态框架,Xcode需要做一些改动,因此为了编译“真”静态框架项目,请在所有的开发环境中安装它(对于使用框架的用户不需要,只有要编译框架才需要)。

The selected run destination is not valid for this action

有时,Xcode出错并加载了错误的active设置。首先,请尝试重启Xcode。如果错误继续存在,Xcode产生了一个坏的项目(因为Xcode4的一个bug,任何类型的项目都会出现这个问题)。如果是这样,你需要创建一个新项目重来一遍。

链接警告

第一次编译框架target时,Xcdoe会在链接阶段报告找不到文件夹:

ld: warning: directory not found for option’-L/Users/myself/Library/Developer/Xcode/DerivedData/MyFramework-ccahfoccjqiognaqraesrxdyqcne/Build/Products/Debug-iphoneos’

此时,可以clean并重新编译target,警告会消除。

Core Data momd not found

对于框架项目和应用程序项目,Xcode会以不同的方式编译momd(托管对象模型文件)。Xcode会简单地在根目录创建.mom文件,而不会创建一个.momd目录(目录中包含VersionInfo.plist和.mom文件)。

这意味着,当从一个embedded framework的model中实例化NSManagedObjectModel时,你必需使用.mom扩展名作为model的URL,而不是采用.momd扩展名。

NSURL *modelURL = [[NSBundle mainBundle]URLForResource:@”MyModel” withExtension:@”mom”];

Unknown class MyClass in Interface Builder file.

由于静态框架采用静态链接,linker会剔除所有它认为无用的代码。不幸的是,linker不会检查xib文件,因此如果类是在xib中引用,而没有在O-C代码中引用,linker将从最终的可执行文件中删除类。这是linker的问题,不是框架的问题(当你编译一个静态库时也会发生这个问题)。苹果内置框架不会发生这个问题,因为他们是运行时动态加载的,存在于iOS设备固件中的动态库是不可能被删除的。

有两个解决的办法:

  1. 让框架的最终用户关闭linker的优化选项,通过在他们的项目的Other Linker Flags中添加-ObjC和-all_load。
  2. 在框架的另一个类中加一个该类的代码引用。例如,假设你有个MyTextField类,被linker剔除了。假设你还有一个MyViewController,它在xib中使用了MyTextField,MyViewController并没有被剔除。你应该这样做:

在MyTextField中:

+ (void)forceLinkerLoad_ {}
在MyViewController中:
+(void) initialize { [MyTextField forceLinkerLoad_]; }

他们仍然需要添加-ObjC到linker设置,但不需要强制all_load了。

第2种方法需要你多做一点工作,但却让最终用户避免在使用你的框架时关闭linker优化(关闭linker优化会导致object文件膨胀)。

unexpected file type ‘wrapper.cfbundle’ in Frameworks &Libraries build phase

这个问题发生在把“假”框架项目作为workspace的依赖,或者把它当作子项目时(“真”框架项目没有这个问题)。尽管这种框架项目产生了正确的静态框架,但Xcode只能从项目文件中看出这是一个bundle,因此它在检查依赖性时发出一个警告,并在linker阶段跳过它。

你可以手动添加一个命令让linker在链接阶段能正确链接。在依赖你的静态框架的项目的OtherLinker Flags中加入:

-framework MyFramework

警告仍然存在, 但不会导致链接失败。

Libraries being linked or not being linked into the finalframework

很不幸, “真”框架和“假”框架模板在处理引入的静态库/框架的工作方式不同的。

“真”框架模板采用正常的静态库生成步骤,不会链接其他静态库/框架到最终生产物中。

“假”框架模板采用“欺骗”Xcode的手段,让它认为是在编译一个可重定位格式的目标文件,在链接阶段就如同编译一个可执行文件,把所有的静态代码文件链接到最终生成物中(尽管不会检查是否确实目标代码)。为了实现象“真”框架一样的效果,你可以只包含库/框架的头文件到你的项目中,而不需要包含库/框架本身。

Unrecognized selector in (some class with a category method)

如果你的静态库或静态框架包含了一个模块(只在类别代码中声明,没有类实现),linker会搞不清楚,并把代码从二进制文件中剔除。因为在最终生成的文件中没有这个方法,所以当调用这个类别中定义的方法时,会报一个“unrecognizedselector”异常。

要解决这个,在包含这个类别的模块代码中加一个“假的”类。linker发现存在完整的O-C类,会将类别代码链接到模块。

我写了一个头文件 LoadableCategory.h ,以减轻这个工作量:

import “SomeConcreteClass+MyAdditions.h”

import “LoadableCategory.h” MAKE_CATEGORIES_LOADABLE(SomeConcreteClass_MyAdditions); @implementation SomeConcreteClass(MyAdditions)

在使用这个框架时,仍然还需要在Build Setting的Other Linker Flags中加入-ObjC。

执行任何代码前单元测试崩溃

如果你在Xcode4.3中创建静态框架(或库)target时,勾选了“withunit tests”,当你试图运行单元测试时,它会崩溃:

Thread 1: EXC_BAD_ACCESS (code=2, address=0×0) 0 0×00000000 — 15 dyldbootstrap:start(…)

这是lldb中的一个bug。你可以用GDB来运行单元测试。编辑scheme,选择Test,在Info标签中将调试器Debugger从LLDB改为GDB。

七:xcode 环境变量 Build Settings参数 workspace 联编设置

分类: iOS应用开发 2013-10-03 21:17 156人阅读 评论(0) 收藏 举报

一、xcode4中的环境变量

\((BUILT_PRODUCTS_DIR)
build成功后的,最终产品路径--可以在Build Settings参数的Per-configuration Build Products Path项里设置
\)(TARGET_NAME)

目标工程名称

\((SRCROOT)
工程文件(比如Nuno.xcodeproj)的路径
\)(CURRENT_PROJECT_VERSION)

当前工程版本号

当编译静态库,设备选模拟器(iPhone 5.0 Simulator),未设置任何Build Settings参数时,默认的基础路径:

/Users/xxx/Library/Developer/Xcode/DerivedData/xxxWorkspace-caepeadwrerdcrftijaolkkagbjf

下面用\(()代替上面一长串东东
\)(SYMROOT) = \(()/Build/Products
\)(BUILD_DIR) = \(()/Build/Products
\)(BUILD_ROOT) = \(()/Build/Products
这三个变量中的\)()不会随着Build Settings参数的设置而改变

相反,以下可以通过设置而改变

$(CONFIGURATION_BUILD_DIR) = \(()/Build/Products/Debug-iphonesimulator
\)(BUILT_PRODUCTS_DIR) = \(()/Build/Products/Debug-iphonesimulator
\)(CONFIGURATION_TEMP_DIR) = \(()/Build/Intermediates/UtilLib.build/Debug-iphonesimulator
\)(TARGET_BUILD_DIR) = \(()/Build/Products/Debug-iphonesimulator
\)(SDK_NAME) = iphonesimulator5.0

\((PLATFORM_NAME) = iphonesimulator
\)(CONFIGURATION) = Debug

\((TARGET_NAME) = UtilLib
\)(EXECUTABLE_NAME) = libUtilLib.a 可执行文件名

\({IPHONEOS_DEPLOYMENT_TARGET} 5.0
\)(ACTION) = build

\((CURRENTCONFIG_SIMULATOR_DIR) 当前模拟器路径
\)(CURRENTCONFIG_DEVICE_DIR) 当前设备路径

\((BUILD_DIR)/\)(CONFIGURATION)\((EFFECTIVE_PLATFORM_NAME =
\)()/Build/Products/Debug-iphonesimulator

\((PROJECT_TEMP_DIR)/\)(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) = \(()/Build/Intermediates/UtilLib.build/Debug-iphonesimulator
自定义变量
\){CONFIGURATION}-iphoneos 表示:Debug-iphoneos

\({CONFIGURATION}-iphonesimulator 表示:Debug-iphonesimulator
\)(CURRENTCONFIG_DEVICE_DIR) = \({SYMROOT}/\){CONFIGURATION}-iphoneos

$(CURRENTCONFIG_SIMULATOR_DIR) = \({SYMROOT}/\){CONFIGURATION}-iphonesimulator

自定义一个设备无关的路径(用来存放各种架构arm6/arm7/i386输出的产品)

$(CREATING_UNIVERSAL_DIR) = \({SYMROOT}/\){CONFIGURATION}-universal

自定义变量代表的值

$(CURRENTCONFIG_DEVICE_DIR) = \(()/Build/Products/Debug-iphoneos
\)(CURRENTCONFIG_SIMULATOR_DIR) = \(()/Build/Products/Debug-iphonesimulator
\)(CREATING_UNIVERSAL_DIR) = $()/Build/Products/Debug-universal

iphoneos5.0下的编译脚本:

xcodebuild -project “UtilLib.xcodeproj” -configuration “Debug” -target “UtilLib” -sdk “iphoneos5.0″ -arch “armv6 armv7″ build RUN_CLANG_STATIC_ANALYZER=NO \((BUILD_DIR)=”\){BUILD_DIR}” BUILD_ROOT=”${BUILD_ROOT}”

iphonesimulator5.0下的编译脚本:

xcodebuild -project “UtilLib.xcodeproj” -configuration “Debug” -target “UtilLib” -sdk “iphonesimulator5.0″ -arch “i386″ build RUN_CLANG_STATIC_ANALYZER=NO \((BUILD_DIR)=”\){BUILD_DIR}” BUILD_ROOT=”${BUILD_ROOT}”

加上下面一句表示输出到文件:

“\({BUILD_ROOT}.build_output”
lipo脚本工具:合并iPhone模拟器和真机的静态类库,生成通用库
lipo -create -output “\){CREATING_UNIVERSAL_DIR}/\({EXECUTABLE_NAME}” “\){CURRENTCONFIG_DEVICE_DIR}/\({EXECUTABLE_NAME}” “\){CURRENTCONFIG_SIMULATOR_DIR}/\({EXECUTABLE_NAME}”
意思是:把”\){CURRENTCONFIG_DEVICE_DIR}目录下的.a文件,和\({CURRENTCONFIG_SIMULATOR_DIR}目录下的.a文件合并,
在\){CREATING_UNIVERSAL_DIR}目录下,生成两个设备都通用的静态库,

例如:lipo -create -output xy.a x.a y.a

二、xcode4中build Settings常见参数解析

1.Installation Directory:安装路径

静态库编译时,在Build Settings中Installation Directory设置“\((BUILT_PRODUCTS_DIR)”
Skip Install设为YES
Installation Directory默认为/usr/local/lib
因为Build Location默认时,.a文件会放在很长(比如:/Users/xxx/Library/Developer/Xcode/DerivedData/xxxProgram
dalrvzehhtesxdfqhxixzafvddwe/Build/Products/Debug-iPhoneos)的路径下,或是我们target指定的路径
Skip Install如果是NO,可能会被安装到默认路径/usr/local/lib
2.Public Headers Folder Path:对外公开头文件路径
设为“include”(具体的头文件路径为:\)(BUILT_PRODUCTS_DIR)/include/xx.h)

在最终文件.a同级目录下生成一个include目录

默认:/usr/local/include

Public Headers Folder Path这个路径就是使用这lib的某工程需要依赖的外部头文件.导入这路径后,#include/import “xx.h”才能看到

3.User Header Search Paths:依赖的外部头文件搜索路径

设置为“\((BUILT_PRODUCTS_DIR)/include”
和2中路径对应
4.Per-configuration Build Products Path:最终文件路径
比如设为“../app”,就会在工程文件.xcodeproj上一层目录下的app目录里,创建最终文件
默认为\)(BUILD_DIR)/\((CONFIGURATION)\)(EFFECTIVE_PLATFORM_NAME)

等于\((BUILT_PRODUCTS_DIR)
5.Per-configuration Intermediate Build Files Path:临时中间文件路径
默认为:\)(PROJECT_TEMP_DIR)/\((CONFIGURATION)\)(EFFECTIVE_PLATFORM_NAME)

6.Code Signing Identity:真机调试的证书选择

选一个和Bundle identifier相对应的证书

Library Search Paths:库搜索路径

Architectures:架构,设为 armv6 或 armv7

Valid Architectures:应用框架,可以设为 armv6、 armv7 或i386

Product Name:工程文件名,默认为$(TARGET_NAME)

Info.plist File:info文件路径

Build Variants:默认为normal

Other Linker Flags:其他链接标签

设为“-ObjC”

当导入的静态库使用了类别,需要设为-ObjC

iOS Deployment Target:ios部署对象

比如可以选择设为,ios3到ios5的一种版本

Prefix Header:预编头文件(比如:UtilLib/UtilLib-Prefix.pch)

Precompile Prefix Header:设为“Yes”,表示允许加入预编译头

三、workspace(工作区)

作用:管理多个工程(project),多工程联编

四、workspace多工程联编设置

1.新建一个静态库工程,比如UtilLib,并生成UtilLib.h和UtilLib.m文件

2.选中需要公开的头文件,

把右侧栏的Target Membership中设置为public

或则,选中工程目录target的Build Phases标签的copy headers项,在public中添加要公开的头文件

3.Architectures设为:armv6 armv7

4.Valid Architectures设为:armv6 armv7 i386

5.Build Products Path设为:\((SRCROOT)/../build
6.Per-configuration Build Products Path设为:
\)(SRCROOT)/../build/\((CONFIGURATION)\)(EFFECTIVE_PLATFORM_NAME)

7.Per-configuration Intermediate Build Files Path设为:

\((SRCROOT)/../build/\)(TARGET_NAME).build/\((CONFIGURATION)\)(EFFECTIVE_PLATFORM_NAME)

8.设置安装路径:Installation Directory项

9.设置对外公开的头文件路径:Public Headers Folder Path项

10.为静态库添加依赖的shell脚本

选中工程目录target的Build Phases标签,点击由下角的Add Build Phase按钮

在弹出的菜单里选择Add run script项,然后页面中会多出一个Run Script项

在黑框里填写”$SRCROOT/mergeArmSymbols.sh”

建立对此脚本的依赖(编译静态库的后会运行此脚本)

如果编译时设备选的是iphone simulator:

则此脚本会在对应iphone device的产品目录Debug-iphoneos中,生成对device有用的.a静态库,

相反,如果设备选的是iphone device:

则此脚本会在对应iphone simulator的产品目录Debug-iphoneos中,生成对simulator有用的.a静态库

最后,此脚本调用lipo工具,把本工程生成静态库与此脚本生成的静态库合并,生成simulator和device都通用的.a文件

11.具体bash shell脚本如下:

mergeArmSymbols.sh

下载右边的图片,然后把后缀改为.sh(其实就是上面的脚本,因为博客园只能上传图片)

静态库编译后的目录结构如下:

1.新建主工程,比如Nuno,添加对静态库的依赖

点击工程,在Build Phases标签的Link Binary With Libraries项中点击加号添加UtilLib.a库

选中上面的红色项,在右边栏的Location选Relative to Project,把值设为../libs/libUtilLib.a

2.设置主工程依赖的外部头文件路径:User Header Search Paths项

\((SRCROOT)/../include
3.设置Header Search Paths为:\)(SRCROOT)/../include

4.设置Library Search Paths为:\((SRCROOT)/../libs
编译运行即可实现联编
(备注:选择模拟器iphone 5.0 simulator,编译静态库的时,最终文件会在Debug-iphonesimulator,就算成功.a文件还是红色,
这是可能是xcode的bug,不会自动切换路径
因为\)(BUILT_PRODUCTS_DIR)所指的位置,是build/Debug-iphonesos,不是包含最终.a文件的Debug-iphonesimulator;

选择ios Device,编译成的最终文件才在build/Debug-iphonesos下,.a文件变成非红色

所有得用mergeArmSymbols.sh脚本来解决)

workspace & subProject & target的更多相关文章

  1. Xcode基本操作

    2.偏好设置 通过“command+,”快捷键或”Xcode|Preferences”菜单呼出偏好设置. (1)主题及字体(Preferences->Fonts & Colors) 选中 ...

  2. 【转】 Xcode基本操作 -- 不错

    原文网址:http://blog.csdn.net/phunxm/article/details/17044337 1.Xcode IDE概览 说明:从左到右,依次是“导航窗格(Navigator)- ...

  3. 【转】 Xcode基本操作

    原文: http://blog.csdn.net/phunxm/article/details/17044337 1.IDE概览 Gutter & Ribbon 焦点列:灰色深度与代码嵌套深度 ...

  4. xcode target

    A target specifies a product to build and contains the instructions for building the product from a ...

  5. macOS 我的装机

    最近多次配置 Mac 的开发环境,稍微记录一下 1 创建无付费信息的Apple ID 2 Xcode ​ gem 源更改 3 Alfred 4 微信 5 SourceTree 6 Sublime Te ...

  6. iOS -- CocoaPods

    CocoaPods 是什么? CocoaPods 是一个负责管理 iOS 项目中第三方开源库的工具.CocoaPods 的项目源码在 GitHub( https://github.com/CocoaP ...

  7. Jenkins+Maven+SVN快速搭建持续集成环境

    http://www.cnblogs.com/sunzhenchao/archive/2013/01/30/2883289.htmlhttp://blog.csdn.net/pein_zero/art ...

  8. Jenkins+SVN+tomcat持续集成发布

    有代码更新后重新打包到tomcat再发布,是不是很烦? 看了下面的东西你就不会烦了. SVN或者git等代码版本控制工具不说了,如果是本地开发,也可以安装一个svn server端 jenkins下载 ...

  9. jenkins 入门教程(中)

    接上回继续,本文以我托管在bitbucket上的一个开源项目spring-boot-rest-framework做为演示,讲解如何创建自动化部署. 一.创建item 点击ok继续,item的详情页面很 ...

随机推荐

  1. 一道来自华为的C机试题目

    题目是这样的 求一个字符串中连续字母的个数 比如I have a book. : 1 I have a dog. : 0 I haavee aa dogg : 4 #include <windo ...

  2. Iphone JS时间

    var end_time = new Date(time).getTime();//月份是实际月份-1  var start_time= new Date(serverTime).getTime(); ...

  3. UML的基本关联

     First, a dependency is a semantic relationship between two model elements in which a change to on ...

  4. Hibernate征途(一)之初识

    相见恨晚 很久以前,大概从开始接触数据库开始,就闪过这样一个想法,怎么看怎么觉得数据库表和vb的类模块很像,不是么?除了vb类模块还有函数外:越往下学觉得二者越像,尤其在三层时学到实体类的概念,我去, ...

  5. swift http请求返回json数据和分析

    1 AppDelegate.swift // // AppDelegate.swift // QQDemo // // Created by 赵超 on 14-6-21. // Copyright ( ...

  6. .net 链接ORACLE的安装包

    odp.net.ma

  7. 用HtmlLink来改变网站的主题

    #region Theme // 注册样式(将主题样式至于通用样式后面) HtmlLink themeCss = new HtmlLink(); themeCss.Href = GetThemeUrl ...

  8. asp.net访问母版页控件方法

    一.使用多种方式为内容页加载母版页默认情况下是给单个内容页指定一个母版页加载,当多个内容页需加载到同一个母版页时,我们可以通过配置文件为多个内容页批量加载母版页,另外,还可以在PreInit事件中动态 ...

  9. Marshal 类的内存操作的一般功能

    Marshal类 提供了一个方法集,这些方法用于分配非托管内存.复制非托管内存块.将托管类型转换为非托管类型,此外还提供了在与非托管代码交互时使用的其他杂项方法. 命名空间:System.Runtim ...

  10. 关于ASP .Net Core 引用dll 一

    一:ASP.Net Core 引用dll文件,不可以直接引用,必须在NuGet中引用才行. 二:如果想引用自己的dll文件,则需要注册NeGet账号,获取到API Key 才行,还需要下载NuGet安 ...