前言
工作中总会遇到一些这样那样的需求, 比如需要打包一个 Appstore版 + 企业版. 但是却总要手动修改 Bundle Id 等等的这些信息, 比较麻烦(当然马甲包也可以这么搞),最重要的万一某一个宏忘记手动切换了,但是测试又没测出来就上架了 Appstore ,那问题就大了. 于是就有了这篇文章.
简介
这里用到的方法主要是对 target 进行复制, 并且使用脚本自动化的修改条件宏来通过 条件编译 达到我们的目的.
复制 target
右键点击需要复制的 target, 点击 Duplicate.就会复制出一份新的 target.

此时选择新的 target 编译运行正常情况是能够直接通过的.
Target 的配置
复制一些需要区别配置或者不同代码的文件.

AppDelegate需要配置一些第三方的 key.比如友盟个推等.为了避免麻烦我直接拿出来分别写Assets.xcassets里面有不同的图片资源, 也需要复制一份.Info.plist工程一些配置信息.main.m里面需要配置AppDelegate.(下面会讲)Main.StoryboardLaunchScreen.storyboard
根据自身需求决定哪些文件需要 copy 新的.
文件配置
因为同一个工程内部不能有 同名的类 , 所以复制出来的 AppDelegate 需要修改一下名称 比如 NewAppDelegate.这里就用到了 main.m.
需要在 main.m 中做如下修改:
1
2
3
4
5
int main(int argc, char * argv[]) {
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([NewAppDelegate class]));
    }
}
类/文件的归属配置:
这里是重点,配置不对将会很麻烦,甚至导致工程跑步起来.
此处以 main.m 为例:
选中 main.m 文件之后, Xcode 右侧区域中会出现 Target Membership 栏目. 如图所示: 
此时选中的 target 就是该文件的归属, 通俗点就是在哪个 target 里面生效. 
同一个文件也可以同时归属多个 target.

大部分文件都可以通过这种方式来设置. 比如: class, xib, storyboard, xcassets 等.
如果编译时候某个类找不到了,就可以在这个地方把对应的 target 勾选中就可以了.
条件宏的自动化
分别创建宏的 头文件 Config.h 和 脚本文件 Config.sh.
配置脚本
在 Build Phases 中点击左上角 + 新建一个 Run Script.

在如图的位置写上执行脚本的路径

1
2
// $PROJECT_DIR 是工程根目录, 根据情况来写
$PROJECT_DIR/Config/Config.sh
脚本
在 Config.sh 中写入如下脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
// 开关配置的头文件路径, 根据情况来
HeaderPath=$SRCROOT/Config/ProjectConfig.h
// 通过判断 Bundle Id 来区分当前编译的是哪个 Target
if [ $PRODUCT_BUNDLE_IDENTIFIER == "com.example.app1" ]; then
    // 把开关宏的值输入到配置文件内
    echo "#define TARGET_SWITCH 0" '// app1'  > $HeaderPath
elif [ $PRODUCT_BUNDLE_IDENTIFIER == "com.example.app2" ]; then
    echo "#define TARGET_SWITCH 1" '// app2' > $HeaderPath
elif [ $PRODUCT_BUNDLE_IDENTIFIER == "com.example.app3" ]; then
    echo "#define TARGET_SWITCH 2" '// app2' > $HeaderPath
else
    echo "// 未配置" > $HeaderPath
fi
此时选择不同的 target ,  不管Run 还是 Build, 在 Config.h 中就会自动在
1
2
3
4
#define TARGET_SWITCH 0
#define TARGET_SWITCH 1
...
#define TARGET_SWITCH n
之间自动切换了.
脚本的原理就是判断不同 target 的 bundle id 在 Config.h 写入不同的宏. 关于脚本怎么写, 可以自己学习一下 Shell
结束
到此, 你就可以根据 TARGET_SWITCH 的值来对不同的 target 区别编译不同的代码了.
补充: 虽然这个比较方便,但是却有可能因为某一个 target 的代码无法编译通过而导致其他的 target 也无法编译通过,因此要做好不同 target 代码的屏蔽.