工程管理篇 | 多Targets实现详述


前言

很多时候,工程管理是一个很实际的技能,我们在实际开发中普遍有很多环境:测试环境、开发环境、生产环境等。还有可能遇到需要创建两个很多内容相同,部分功能不同的工程。

如果需要切换环境发版本的话,你可以手动注册修改代码实现(如果你喜欢这样的话,也就不需要继续看本文了),不是说这样很low,而是当不同环境的差异比较多的话,这种手动管理工程版本的方法笨拙而且容易出错。

创建两个很多内容相同,部分功能不同的工程,你可以选择拷贝,修改,保存,成为两个工程。当然还有另一种优雅的姿势。

这个姿势挺累的

这些问题都可以通过设置多个 Targets 来解决。

其它知识补充

workspace 、Project、target、 Scheme 的关系和简介

workspace 是Xcode的一种文件,用来管理工程和里面的文件,一个workspace可以包含若干个工程
project 里面包含了所有的源文件,资源文件和构建一个或者多个product的信息。project利用他们去编译我们所需的product,也帮我们组织它们之间的关系。一个project可以包含一个或者多个target。
target 每个target都继承了project的默认设置,每个target可以通过重新设置target的编译选项来定义自己的特殊编译选项。一个target对应于一个product(产品)
scheme 定义了编译集合中的若干target,编译时的一些设置以及要执行的测试集合。我们可以定义多个scheme对应一个target

关于Target

相信很多人都注意到XCode中, 有个Target的概念.那么这个Target到底是什么呢?

Apple的人是这样说的:“ Targets that define the products to build. A target organizes the files and instructions needed to build a product into a sequence of build actions that can be taken.”

简单的理解的话, 可以认为一个target对应一个新的product(基于同一份代码的情况下). 但都一份代码了, 弄个新product做什么呢 ?

其实这不是单纯的瞎折腾, 虽然代码是同一份, 但编译设置(比如编译条件), 以及包含的资源文件却可以有很大的差别. 于是即使同一份代码, 产出的product也可能大不相同.

Targets之间, 什么相同, 什么不同!

既然是利用同一份代码产出不同的product, 那么到底不同Target之间存在着什么样的差异呢?
要解释这个问题, 我们就要来看看一个Target指定了哪些内容.

从XCode左侧的列表中, 我们可以看到一个Target包含了Copy Bundle Resources, Compile Sources, Link Binary With Libraries. 其中

  • Copy Bundle Resources 是指生成的product的.app内将包含哪些资源文件

  • Compile Sources 是指将有哪些源代码被编译

  • Link Binary With Libraries 是指编译过程中会引用哪些库文件

    Paste_Image.png

通过Copy Bundle Resources中内容的不同设置, 我们可以让不同的product包含不同的资源, 包括程序的主图标等, 而不是把XCode的工程中列出的资源一股脑的包含进去。

而这还不是一个target所指定的全部内容. 每个target可以使用一个独立, 不同的Info.plist文件。

我们都知道, 这个Info.plist文件内定义了一个iPhone项目的很多关键性内容, 比如程序名称, 最终生成product的全局唯一id等等。

而且不同的target还可以定义完整的差异化的编译设置, 从简单的调整优化选项, 到增加条件编译所使用的编译条件, 以至于所使用的base SDK都可以差异化指定.

两种添加Targets的方式

拷贝原有的Target

项目里面创建了多个target(分别是:生产环境、测试环境、开发环境),每个target对应一个环境,并配置不同的info.plist文件,这样做的好处是不用开发人员每次都要去手动开启/注释某些代码去发布,而是先配置好,到时候直接切换target就可以打包上线了切换一下target,运行一下,就是一个新项目。

第一步:

弹出来的的页面是:一般选择中间那个选项 Duplicate only,如果有pad,则选择最后一个


第二步:



第三步:(这里不要忘了把你原来的Info.plist文件也勾选对了)

第四五六七步:

通过在不同的Targets 预定义宏(Build Setting–>Preprocessor Macros)区分不同的工程环境

这个宏是一个全局宏,在所有/整个工程的代码中都是有效的,我们可以在这里添加上环境与处理的宏(例如:WD_Environment_Mode,这个是随便自己取的)

#然后在.pch文件中
/**
WD_Environment_Mode
0:生产环境
1:测试环境
2:开发环境
*/
#ifdef    WD_Environment_Mode
#if       WD_Environment_Mode == 0
#define    WDAPIServerUrl   @""
#define    JSPatchAppKey     @""

#elif      WD_Environment_Mode == 1
#define    WDAPIServerUrl @""
#define    JSPatchAppKey  @""

#elif    WD_Environment_Mode == 2
#defineWDAPIServerUrl @""
#defineJSPatchAppKey  @""
#else
#warning"未匹配环境"
#endif

***************************************
更多关于 预定义宏的使用

而 Xcode 在產生新的 project 時,會自動在 Debug scheme 裡面加入 DEBUG=1 這個 Preprocessor Macro.
因此可以使用 DEBUG 這個 preprocessor macro 來區分 debug 和 release mode.


  ViewController.m
  ...
  #ifdef DEBUG
      [self.hintLabel setText:@"Debug mode"];
  #else
      [self.hintLabel setText:@"Release mode"];
  #endif
  ...

如果你不喜欢、不习惯这样的代码写法 可以在 PrefixHeader.pch 裡面加入以下的片段:

  PrefixHeader.pch

  #ifdef DEBUG
  #define debug_only YES
  #else
  #define debug_only NO
  #endif

  ViewController.m
   ...
  if(debug_only){
      [self.hintLabel setText:@"Debug mode"];
  } else {
      [self.hintLabel setText:@"Release mode"];
  }
  ...

就可以在所有的 source code 都直接用 if(debug_only){ ... } 來將 debug 時才會用到的程式片段包起來了。这是你最熟悉的方式是吧.

****************************************

总结一下,上面这种复制 target的方式比较适合不同的环境需要用到的变量值不同,也就是通过不同的 Target里面的 全局宏的值来做判断依据的,但是整套工程还是只有一套代码,相当于做了条件编译。

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

  #if defined (MACRO)
       //target  B需要执行的代码
  #else
       //target A需要执行的代码
  #endif

来区分,并且同时又可以通过新Targets 来实现APP的 Logo,启动图,App名称的个性化定制!

创建全新的target

有的时候,我们创建两个很多内容相同,部分功能不同的工程,你可以选择拷贝,修改,保存,成为两个工程。当然你也可以利用 多个 Target实现。

但是有时候,两个版本里面的资源是冲突的,不能同时导入到一个target,上面我们通过复制 Target实现的其实是条件编译,并不是真正的新Target。下面的方式就能解决上面提到的冲突的问题。

最赞的是,这种方式即可以解决导入资源冲突的问题,又可以把公用的部分拿出来,供两个Target使用,真正做到了:只修改或者创建不同的功能,公用相同的功能。

Paste_Image.png

会出现一个弹框,向下滚动,选择Single View Application-->Next-->Produce Name -->Finish

Paste_Image.png

但是这样生成出的Target几乎是空的. Copy Bundle Resources, Compile Sources, Link Binary With Libraries里面都没有任何内容. 编译设置也是完全原始的状态。等于是一个新的小工程。

下面就在一个复杂成熟系统中新增target的真实操作为案例详述实际中的操作

需求场景:当时是因为需要用海康的新版SDK做一个APP,但是这个APP的其余部分都跟以前的APP是一样的,于是就想到了新增target,通过共用公共模块,不同target 实现不同的视频监控功能。

基础知识补充

  • 【1】Xcode左侧的文件不一定都是实际目录下存在的,有的只是别的目录位置下的文件的引用。
  • 【2】Xcode左侧的文件不一定属于看上去的目录,没有出现的文件也一定不能被该 Target使用。
  • 【3】修改Xcode左侧的实际存在的文件/文件夹位置会直接修改文件的实际物理位置。
  • 【4】如果一个工程下(即 project的文件夹下)已经存在了某一个文件 .h 或者.m时,当该文件再次被拖进工程中时,就算我们勾选了“Copy items if needed”该文件也看似新增了并出现在了Xcode的左侧导航中,但是这个“新增”的文件却是一个引用文件,并非重新创建了一份。
  • 【5】工程文件中添加生产新文件的方式只有两种:
    • A.原工程中没有该文件并且在拖进工程中时 勾选了“Copy items if needed”。
    • B. 手动复制并生成在实际物理位置下,再拖入并引用至工程中。

步骤:

一、可以梳理工程中的公用文件到一个公共文件夹中,注意这个时候因为移动了文件的实际物理位置,我们可以通过先移除对文件夹的引用 Remove References,注意不是 Move to Trash


文件夹的实际位置从A移动到了B



当我们整理好公用部分的文件后再次拖入工程后,即便我们如下图那样选择了Add to targets 到所有的 targets后:



公共文件夹中的文件还是没有被引用到任何的 target中

二、给新 Target 的 Compile Sources 、Link Binary With Libraries 、Copy Bundle Resources 中添加需要的文件引用。

  • Compile Sources中添加需要的所有 .m/.mm 文件,不需要添加进 .h 文件,这个后面会说。
  • Link Binary With Libraries 中添加的是需要的所有静态库、系统类库。
  • Copy Bundle Resources 中添加所有需要的图片或者其他资源文件 包括:xib、 storyBorad。
    基本上上面三个位置就是一个 Target 的所有能引用到的资源设置。

并设置好特殊类的编译设置:


三、设置新 Target 的 info.plist 文件和 .pch文件路径,注意.pch文件路径 就是该文件在工程目录中的相对实际物理路径


比如上面的这张图中:.pch的文件路径就是 Modular/GlobalConfig/xxxxx.pch

四、注意事项

1、 两个不同 Target中可以存在相同名称的 .h 和 .m 文件,不会出现重名的冲突。
2、关于.h文件,有一个很有意思的事情就是,我们在引用设置的时候不用管任何 .h 文件,一般第三方中会有大量的 .h文件,我们都不需要管,在新的 Target 中如果没有对应名称的 .h 文件,编译器就会从整个工程目录中搜索,如果在其他的Target 中找到对应的 .h文件就会直接引用,如果在当前 Target中搜索到对应名称的 .h文件就会引用当前Target内的 .h文件,即便别的Target中也有相同名称的 .h 文件也不会被引用。
3、当然你也可以不整理出公用的公共部分文件,这样就直接 new一个 Target ,完成 Bouild Phases的资源引用设置后,设置好 info.plist 文件和 .pch文件路径就完成了多 Target 的工程设置了。


本文参考文章
手把手教你给一个iOS app配置多个环境变量
使用 Preprocessor Macros 區分 release 和 debug 版本
如何在iOS项目中创建多个target
iOS新建target,使两个不同项目共用某一模块

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容