背景:很多时候,我们需要在一个工程中创立多个target,也就是说我们希望同一份代码可以创建两个应用,放到模拟器或者真机上,或者是,我们平时有N多人合作开发,当测试的时候,在A这里装了一遍测A写的那块,当需要测试B写的代码时,我们需要到B那里去装一遍,如果只有一个target的话,那么A的将会被覆盖

还有些时候,我们需要确定到底是A的问题还是B的代码出了问题,这时候都需要建立一个工程能够编译多个版本出来,下文就介绍怎么在一个工程中编译多个版本
 
 
好了,闲话不多少,下面正式开始:
我们建立一个默认的应用来演示做法:
建立后的应用如下:
 
可以看到,上面只有一个target:即TestTarget,为了方便演示,我们给它加上一个多语言,让它在模拟器上的名字是中文
我们在工程的InfoPlist.strings中增加一句:CFBundleDisplayName="测试正式版”;
这样在模拟器中安装后显示的是
 
好了,前序工作已经可以了
 
1.新增一个target,因为这里我们是建立一个和原来基本一致的target,所以,我们这里可以选择复制来新建一个target
当你选择复制后,会多出两个文件,如下
还会在scheme那里多出一个和这个target相关的scheme
 
 
2.
上面的名字太难看,我们改个名字,改了名字后,你会发现
重新选择我们更改后名字的Info plist文件 TestTarget2-info.plist文件(文件名最好是包含info.plist,这个是一个约定,便于以后寻找这个文件)
我们将那个scheme也更改一下
 
还有生成的product的名字也要改一下,切换到TestTargetTest2的Build Settings下,搜索product name,将product name改成我们需要的名字(注意,这个名字只是我们生成的app的名字,不一定是最后的显示的名字,最后显示的名字还要看用户是否在InfoPlist.strings文件中设置了CFBundleDisplayName="测试1";)
 
 
 
3.切换到TestTargetTarget2这个scheme,运行,
看看模拟器中,你会发现
    模拟器中有两个测试1了,
 
有些同学在这里并没有生成两个 图标,这个应该是他将两个target的Bundle Identifier都设成了一样的,这个如果一样的话,那生成的target会覆盖上一次的
 
4.上面已经生成了两个target,并且也在模拟器上运行成功了,问题来了,我们怎么知道哪个是哪个呢,两个的名字是一样的,不要担心,这个问题很好解决的
 
由于共用了一个InfoPlist.stirngs文件,所以,才会出现两个target在模拟器上都显示为 测试1,知道了问题所在,那么下面我们就解决它,很显然,我们应该针对TestTargetTest2再建立一个InfoPlist.stirngs,
由于我们只是需要名字显示不同,其它都不需要改变的,所以我们可以直接进行复制,将工程目录下的所有的语言的 InfoPlist.strings文件复制一份,建立一个新的文件夹(注意,需要将所有语言的InfoPlist.strings都复制一份,因为多语言嘛,语言的文件夹也要有)
如图
由于我们这里,只有一种语言,
,所以我们targetTest2中只有一个en.lproj文件夹,将targetTest2文件夹下的InfoPlist.strings中的 
 
 
 
将刚添加的文件加到工程中来
添加完后,将原来那个Infoplist.stirngs的target中的targetTest2去掉如下图:
 
将目录targetTest2下的Infop.stirngs中的内容修改成 测试2
 
5.先选TestTarget这个scheme,运行,在模拟器上会出现 测试1,切换scheme为TestTarget2,运行,在模拟器上将生成 测试2
切换scheme如图
一切正确后在模拟器上将会显示
 
 
 
6.作为附录,其实也比较重要的是,我们很多target共用的代码,资源等,有时候我们在代码中需要区分到底是哪个target,比如说,我们生成的第二个target是一个受限版本,我们需要提示用户(比如是功能受限的免费版本),怎么在代码中区分呢?
有如下三个方式
第一种方式,利用CFBundleIdentifier来判断
  1. NSString*BundleIdentifier =[[[NSBundlemainBundle]infoDictionary]objectForKey:@"CFBundleIdentifier"];// Do any additional setup after loading the view, typically from a nib.
       
    if ([BundleIdentifierisEqualToString:@"yohunl.TestTarget2"])
    {
           
    //处理代码
           
    NSLog(@"TestTarget2-Info.plist");
        }
       
    else {
           
    //处理代码
           
    NSLog(@"TestTarget-Info.plist");
        }

    第二种方式,定义一个编译器宏,来进行区分,在
    打开TestTarget2,
注意,上面的 -D是需要的,一般我们对于这种定义宏都大写的
在代码中可以

#ifdef TARGET2
    //target2的处理代码
    NSLog(@"TARGET2");
#else
    NSLog(@"TARGET1");
#endif

 
 
第三种方式,这种不需要增加-D的
增加预编译宏
在代码中

#ifdef TESTTARGET2
    //target2的处理代码
    NSLog(@"TESTTARGET2");
#else
  
    NSLog(@"TARGET1");

#endif
 
以上三种方式都可以的
 
 
附录二:参考文献
 
 

相信很多人都注意到XCode中, 有个Target的概念. 这在很多地方都有所体现, 比如打开一个工程后, 左侧的列表中有Targets一项, 而在工程界面的顶部菜单中, project里面也有多个涉及到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的情况吧, 比如完整版和lite版; 比如同一个游戏的20关, 30关, 50关版; 再或者比如同一个游戏换些资源和名字就当新游戏卖的(喂喂, 你在教些什么...)。

以上copy的。

这里不是要讨论如何制作这样的工程,而是选择做与不做。

最近的工作主题时维护制作两个不同版本的代码,相当是完整版和简版的两个项目。

现在我们的处理是放在两个不同目录,以前两个项目是由不同的框架组成,现在经过升级后,很多基础功能都是一样的,但也还是保留放在两个不同地方维护。我一边做一边在想,为什么一样的代码却要放在两个目录下,那时已经发现的bug,只有一个地方得到修复了,这边还是没有的。实现同样的功能,却使用了两份不同的代码和框架。

我理想的情况是,我们只有一个工程,这个工程应该就是一个完整工程,它包含了多个target,编译一次它同时或者单独输出各个不同版本的target即可。

但是,这次升级,我把iphone和ipad的代码和在一起,后来自己测试时,我却有些犹豫了。版本做成,iphone4.0运行的好好的,上ipad,崩了,上iphnoe3.0,起不来。于是代码中出现好多重复的这样的代码

If (isipad)

xxx

else

xxx

我开始在犹豫了。现在只是iphone和ipad放在一起,如果把两个不同版本的target放在一起,那也许更惨了。3.0编译的,要测试ipad的,iphone3,iphnoe4;4.0编译的同样也是。一次改动,所有的都要测试,那维护起来该有多么的小心翼翼,如履薄冰啊。而且代码也变得越来越脏了。

面对经常遇到复杂而又频繁多变的无聊小需求,把鸡蛋放到一个篮子里,或许真不是一个好的选择。相反的我倒是觉得也许现在没有将这两个工程和在一起是一个正确的选择。

取舍之间,我还是比较倾向于合并在一起维护。因为每一次出现一个小的适配,如果忘记两个地方都修改,出现一个又一个深藏的bug,最后应该是满目疮痍。

以前听说在做s60的软件都是单独几种设备适配一个版本,也就是说,需要维护同样的很多分代码,这对于我来说简直是噩梦。

不过做软件就是这样吧,该合的合,该分的还是需要分清楚的,一定要分清楚。

最新文章

  1. r-cnn学习(六):RPN及AnchorTargetLayer学习
  2. IOS开发证书显示“此证书的签发者无效”解决方法
  3. BroadcastReceiver应用详解(转)
  4. 模拟 ACdream 1196 KIDx's Pagination
  5. poj1942 Paths on a Grid
  6. U盘安装Win7 64位系统(笔记本+台式机亲测)
  7. Inside of Jemalloc
  8. iperf linux版本移植到android (使用工具链方式不是使用Android.mk)
  9. PHP完成一个日历
  10. telematics product and company in China
  11. 前端技术之_CSS详解第四天
  12. JS正则表达式匹配域名 网址 URL
  13. odoo10 addon开发流程
  14. 为什么ArrayList、LinkedList线程不安全,Vector线程安全
  15. multiwan 脚本
  16. 更换gcc工具链
  17. 遍历文件,读取.wxss文件,在头部添加一条注释
  18. gitlab 10安装
  19. mysql的安装和配置
  20. ASI 实现注册方法的小例子(get和post方式)

热门文章

  1. linux笔记(1)
  2. LA 7049 Galaxy 枚举
  3. Map容器——HashMap及常用API,及put,get方法解析,哈希码的产生和使用
  4. 【java基础 13】两种方法判断hashmap中是否形成环形链表
  5. mq类----2
  6. HDU-1534 Schedule Problem
  7. NOIP2017赛前模拟1:总结
  8. faster-rcnn 目标检测 数据集制作
  9. bootstrap 事件shown.bs.modal用于监听并执行你自己的代码【写hostmanger关联部门遇到的问题及解决方法】
  10. 关于后台返回excel文件的问题