前言
一個(gè)APP的開發(fā)涉及到多種環(huán)境在所難免,比如Test環(huán)境,UAT(User Acceptance Test)用戶驗(yàn)收測(cè)試環(huán)境,Release環(huán)境等等。我的項(xiàng)目需求是,在不同的環(huán)境下在代碼中使用全局性常量值不同,比如說請(qǐng)求所用的IP。這里只是作一個(gè)簡單的記錄和分享。
Configuration配置
配置Configuration的目的是為了增加或刪除編譯環(huán)境,對(duì)于不同編譯環(huán)境下的參數(shù)需求可以到xcconfig中進(jìn)行設(shè)置。
1、進(jìn)入工程的info頁,下面紅框中標(biāo)明的是工程創(chuàng)建時(shí)自帶的兩個(gè)編譯環(huán)境Debug和Release。
點(diǎn)擊下面的加號(hào)添加新的編譯環(huán)境,根據(jù)需要選擇Duplicate Debug和Duplicate Release,他們的區(qū)別在于,后者會(huì)進(jìn)行各種優(yōu)化,包括編譯的優(yōu)化,導(dǎo)致不能調(diào)試(不能通過斷點(diǎn)、LLDB查看變量),另外Release環(huán)境下打的包也會(huì)更輕量一些。這里我將Debug環(huán)境名改為UAT_α,并增加一個(gè)基于Debug 的UAT_β環(huán)境。
插曲:如果想要在Relase環(huán)境下去調(diào)試可以前往Bulid Setting總進(jìn)行設(shè)置,將Release下的參數(shù)換成和Debug一樣即可:
2、配置預(yù)編譯宏,這就相當(dāng)于在pch文件當(dāng)中去定義宏,可以在項(xiàng)目中做不同環(huán)境的區(qū)分,進(jìn)而進(jìn)行操作。進(jìn)入工程的Build Setting ->Preprocessor Macros:
我們看到UAT_α和UAT_β對(duì)應(yīng)的值均為DEBUG = 1,我們可以理解的是在兩種環(huán)境下同時(shí)定義了#define DEBUG 1,為了表示區(qū)別可以分別設(shè)置為不同的宏名和值,例如:ConfigDemo_ UAT_α=0,ConfigDemo_ UAT_β=1(等號(hào)兩邊不能留空格,否則會(huì)編譯出錯(cuò))。GCC提供了一種動(dòng)態(tài)編譯的方法:ConfigDemo_{CONFIGURATION}=1,這兩種設(shè)置方法是等價(jià)的(注意不要?jiǎng)?(inherited),他表示繼承工程的默認(rèn)設(shè)置)。
3、進(jìn)行Scheme的配置,可以理解一個(gè)Scheme對(duì)應(yīng)一個(gè)Target、一種環(huán)境(當(dāng)然這里只有一個(gè)Target,只是編譯環(huán)境不同)。 當(dāng)我們切換不同的Scheme運(yùn)行時(shí)就是切換不同的環(huán)境了。在Xcode的左上角選擇添加新的Scheme,并分別添加ConfigDemo_ UAT_α,ConfigDemo_ UAT_β兩個(gè)Scheme:
添加完Scheme后對(duì)其進(jìn)行配置,我們剛才配置環(huán)境后總共有Release、UAT_α、UAT_β三種環(huán)境,在這里使他們分別對(duì)應(yīng)ConfigDemo、ConfigDemo_ UAT_α,ConfigDemo_ UAT_β,選擇Edit Scheme,這里可以將Run、Test、Archive改為對(duì)應(yīng)的Scheme,關(guān)于他們的解釋:
于是在代碼中就可以進(jìn)行如下操作:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
#ifdef ConfigDemo_UAT_α
#define TestString @"ConfigDemo_UAT_α"
#elif ConfigDemo_UAT_β
#define TestString @"ConfigDemo_UAT_β"
#else //Release
#define TestString @"Release"
#endif
NSLog(@"TestString:%@",TestString);
// 打印結(jié)果分別為Release、ConfigDemo_UAT_α、ConfigDemo_UAT_β
}
根據(jù)不同的Scheme配置不同的displayName、APPICON等可以參見文章最后的鏈接;
配置xcconfig
關(guān)于項(xiàng)目的參數(shù)需要做一下說明:
xcconfig文件的修改實(shí)際上是修改build setting中的參數(shù)。Projects 會(huì)包含一個(gè)或者多個(gè) targets,這里可以理解每一種編譯環(huán)境對(duì)應(yīng)一個(gè)target,每一個(gè) target 將會(huì)產(chǎn)出一個(gè) product.這些指令以 build setting 和 build phases 的形式存在,你可在 Xcode 的項(xiàng)目編輯器(TARGETS->Build Setting, TARGETS->Build Phases)中進(jìn)行查看和編輯。target 中的 build setting 參數(shù)繼承自 project 的 build settings, 但是你可以在 target 中修改任意 settings 來重寫 project settings,這樣,最終生效的 settings 參數(shù)以在 target 中設(shè)置的為準(zhǔn). Project 可包含多個(gè) target, 但是在同一時(shí)刻,只會(huì)有一個(gè) target 生效,可用 Xcode 的 scheme 來指定是哪一個(gè) target 生效,雖然這里并沒有配置多個(gè)Target,但是這里還是可以理解一個(gè)Scheme對(duì)應(yīng)一個(gè)Target。
另外要說明一點(diǎn),對(duì)于項(xiàng)目中其他地方的配置,可以直接在操作板中復(fù)制粘貼到文件中來進(jìn)行修改,最終xcconfig的設(shè)置會(huì)覆蓋原來的設(shè)置。
對(duì)于需要單獨(dú)配置參數(shù)的環(huán)境則生成一個(gè)xcconfig文件,沒有單獨(dú)配置xcconfig的則使用默認(rèn)的設(shè)置,另外在這里需要生成一個(gè)公用的xcconfig文件。公共文件用于定義好統(tǒng)一的變量(宏),每一個(gè)環(huán)境去設(shè)置具體的值,形成多環(huán)境配置。
每個(gè)文件代碼的寫法:
- Common. xcconfig的寫法
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) VariableA='$(VariableA)' VariableB='$(VariableB)'
解析如下:
其作用是將配置文件中定義的常量定義成預(yù)編譯宏,以便于在代碼中獲取。
GCC_PREPROCESSOR_DEFINITIONS 是 GCC 預(yù)編譯頭參數(shù),對(duì)應(yīng)的設(shè)置在 Xcode8下的路徑為 Build Settings->Apple LLVM 7.x Preprocessing->Preprocessor Macros,就是我上面提到的設(shè)置預(yù)編譯宏,這里權(quán)且可以理解上面這一串代碼是在Build Setting中設(shè)置預(yù)編譯宏的代碼化,而這里的設(shè)置并不會(huì)覆蓋之前在Build Setting中的設(shè)置,只會(huì)將這些新增的設(shè)置添加到最后面。 上面代碼的意思是,定義兩個(gè)預(yù)編譯的宏名字分別為VariableA、VariableB,他們具體的值就可以在不同的xcconfig中去設(shè)置,不同的xcconfig需要導(dǎo)入這一個(gè)Common. xcconfig的文件,這就實(shí)現(xiàn)了不同環(huán)境下動(dòng)態(tài)的配置,不同環(huán)境下的預(yù)編譯宏名一樣只是值會(huì)不一樣,而且這些xcconfig文件可以在不同的項(xiàng)目中使用。
這樣就可以簡單的理解為Common. xcconfig實(shí)現(xiàn)了一個(gè)Build Setting里面的一個(gè)方法,而其他的xcconfig文件調(diào)用這個(gè)方法進(jìn)行具體設(shè)置。
上面在Preprocessor Macros中配置的用于在代碼中區(qū)分不同環(huán)境的預(yù)編譯宏,也可以直接在Common. xcconfig中設(shè)置,只是在代碼中進(jìn)行區(qū)分就要區(qū)分不同宏的值了。
2.其他xcconfig代碼:
#include "Common.xcconfig"
VariableA = @"UAT_α.xcconfig_VariableA"
VariableB = @"UAT_α.xcconfig_VariableB"
3、將xcconfig與環(huán)境進(jìn)行關(guān)聯(lián):
我們來到工程下的info->Configuration,
我們發(fā)現(xiàn),每一種環(huán)境下都有兩個(gè)這樣的設(shè)置,一個(gè)設(shè)置是工程的xcconfig文件配置,另一個(gè)則是target的xcconfig文件配置,在這里配置xcconfig意味著APP在運(yùn)行的時(shí)候會(huì)去加載相應(yīng)xcconfig中的設(shè)置。上面我們說到了配置的繼承關(guān)系,那么這里只設(shè)置target的xcconfig就夠了,而且你設(shè)置了工程的xcconfig就相當(dāng)于在編譯的時(shí)候?qū)⑦@些xcconfig的配置加載設(shè)置了兩遍!其實(shí)你也會(huì)發(fā)現(xiàn)此時(shí)再設(shè)置工程的xcconfig也沒有關(guān)系。這里我暫且只設(shè)置UAT_α和UAT_β環(huán)境下的xcconfig,Release環(huán)境留著調(diào)式pod。
2019-6-7更新:
經(jīng)過上面的設(shè)置,在某些Xcode上會(huì)出現(xiàn)“use undecalred indentifire”的錯(cuò)誤,經(jīng)過不斷嘗試后發(fā)現(xiàn),將工程的xcconfig文件對(duì)應(yīng)為Commn就可以了,猜測(cè)與Xcode自身有關(guān),配置文件的加載順序會(huì)影響到代碼的編譯。
接下來你就可以直接在代碼中使用VariableA、VariableB宏了:
// 這里并不能在Release環(huán)境下使用這些宏,否則會(huì)報(bào)錯(cuò)。
NSLog(@"%@ - %@",VariableA, VariableB);
// ConfigDemo_UAT_α、ConfigDemo_UAT_β環(huán)境打印結(jié)果分別為
// UAT_α.xcconfig_VariableA - UAT_α.xcconfig_VariableB
// UAT_β.xcconfig_VariableA - UAT_β.xcconfig_VariableB
Pod調(diào)試,Pod安裝SDWebImage
在刪除上面的log代碼之后,你會(huì)發(fā)現(xiàn)只有在Release環(huán)境下能夠運(yùn)行!首先我們來看看報(bào)錯(cuò):
是的,工程找不到相關(guān)文件了,其實(shí)這個(gè)你在終端的提示中就可以發(fā)現(xiàn)端倪:
大概意思是你需要你配置的xcconfig文件中導(dǎo)入pod配置文件的路徑。
在完成一次pod install之后,Pods工程會(huì)為每一個(gè)環(huán)境生成一個(gè)xcconfig文件,包括默認(rèn)的debug環(huán)境,如果我們的目標(biāo)工程ConfigDemo對(duì)應(yīng)的環(huán)境沒有配置xcconfig文件,那么就會(huì)在ConfigDemo工程下拷貝一份對(duì)應(yīng)的xcconfig文件并自動(dòng)在環(huán)境中完成配置,這種配置讓工程能夠使用pod。大家可以看到這里在ConfigDemo工程下只有Release. xcconfig文件的拷貝,而此時(shí)Release環(huán)境下的Configuration配置已經(jīng)發(fā)生了改變。
我們只需要在其他xcconfig文件中導(dǎo)入相關(guān)的pod下的xcconfig就好了,記得使用相對(duì)路徑,否則在其他電腦上就不能運(yùn)行了:
#include "Pods/Target Support Files/Pods-ConfigDemo/Pods-ConfigDemo.release.xcconfig"
這個(gè)時(shí)候你就可以刪除在ConfigDemo工程下的.release.xcconfig文件,并去Configuration中配置你自己的xcconfig文件了,記得重新pod install一下。
Demo:https://github.com/Randy1993/ConfigDemo
宏DEBUG丟失的問題
上述的配置結(jié)束之后,會(huì)導(dǎo)致Pod工程下UAT_α等缺少DEBUG宏,導(dǎo)致某些三方庫無法正常使用,比如說MLeaksFinder
無效。在Podfile中添加如下代碼即可:
pod 'MLeaksFinder'
post_install do |installer_representation|
installer_representation.pods_project.targets.each do |target|
if target.name == 'MLeaksFinder'
target.build_configurations.each do |config|
if config.name == 'UAT_α'
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= ['$(inherited)','DEBUG=1']
end
end
end
end
end
參考文章:
http://www.lxweimin.com/p/83b6e781eb51
http://liumh.com/2016/05/22/use-xcconfig-config-specific-variable/#xcconfig-environment 關(guān)于xcconfig文件的說明。
http://www.lxweimin.com/p/51a2bbe877aa Configuration配置