網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
引言
利用Xcode
進(jìn)行開(kāi)發(fā)時(shí)需要進(jìn)行很多build setting
的設(shè)置以便能讓項(xiàng)目按照設(shè)置的進(jìn)行編譯,同時(shí)有時(shí)候需要在編譯時(shí)利用script
腳本進(jìn)行一些設(shè)置,本文主要介紹xconfig
文件和script
腳本在Xcode
開(kāi)發(fā)中使用。
Xcode編譯
在使用xconfig
時(shí)有幾個(gè)關(guān)于Xcode
的概念是需要理解的,這里我進(jìn)行通俗簡(jiǎn)單的說(shuō)明,同時(shí)需要知道Xcode
在編譯的過(guò)程中具體幫我們做了那幾件事情。
Xcode target
在實(shí)際開(kāi)發(fā)中一個(gè)Xcode
創(chuàng)建的項(xiàng)目是可以有多個(gè)taget
的,比如我們創(chuàng)建一個(gè)widget
時(shí)Xcode
會(huì)自動(dòng)新建一個(gè)target
對(duì)應(yīng)這個(gè)widget
,也可以自己新建,同一個(gè)項(xiàng)目有多個(gè)target
可以滿(mǎn)足不同的測(cè)試場(chǎng)景,比如在前期開(kāi)發(fā)階段使用一個(gè)target
,到UAT
階段使用另外一個(gè)target
。一個(gè)target
對(duì)應(yīng)一個(gè)product
,也就是編譯后安裝到手機(jī)上的項(xiàng)目,target
定義了生成的唯一 product
, 它將構(gòu)建該product
所需的文件和處理這些文件所需的指令集整合進(jìn) build system
中,這些指令以 build setting
和 build phases
的形式存在,我們用xconfig
文件來(lái)設(shè)置 build setting
,同時(shí)將script
腳本添加到build phases
中。
新建target
Xcode project
Xcode project
是一個(gè)倉(cāng)庫(kù),該倉(cāng)庫(kù)包含了所有的文件,資源和用于生成一個(gè)或者多個(gè)software products
的信息,它包含一個(gè)或者多個(gè)targets
,其中的每一個(gè) target
指明了如何生成 products
。project
為其擁有的所有 targets
定義了默認(rèn)的build settings
,例如project
中默認(rèn)包含debug
和release
兩種build settings
當(dāng)然,每一個(gè) target
能夠制定其自己的 build settings
,且target
的build settings
會(huì)重寫(xiě)project
的 build settings
。
Xcode scheme
一個(gè)project
可以有多個(gè)target
,但是當(dāng)前的target
只能有一個(gè),scheme
就是用來(lái)確定當(dāng)前的target
的,并制定當(dāng)前的target
使用哪種configuration
。
新建configuration
打開(kāi)項(xiàng)目編輯欄選擇上面的progect
同時(shí)選擇info
欄,可以看到Xcode
默認(rèn)添加了二個(gè)Debug
和Release
的configuration
,點(diǎn)擊做下角的+
號(hào)按鈕選擇復(fù)制Debug
或者Release
其中一個(gè)configuration
來(lái)新建并命名一個(gè)自己想取的名字,我這里命名為Mamba
。
Configuration文件的使用
平時(shí)手動(dòng)的在Xcode
中進(jìn)行項(xiàng)目的一些build setting
設(shè)置還是比較麻煩的,一個(gè)是需要在Xcode
中進(jìn)行搜索,另外一個(gè)是不好管理,例如需要在debug
或者release
下進(jìn)行不同的設(shè)置的話(huà)就比較麻煩。利用Configuration
文件來(lái)代替手動(dòng)設(shè)置則更加的方便,直接新建Configuration Setting file
類(lèi)型文件,如下圖所示:
利用Configuration設(shè)置不同的項(xiàng)目名
Configuration
文件是可以繼承的,一般先建立一個(gè)Common Configuration
文件用來(lái)作為父類(lèi),為此新建一個(gè)名為Common
的Configuration
文件,并加入如下代碼:
APP_NAME = TestDemo
然后分別新建名為debug
,Mamba
和release
的Configuration
文件,并加入如下代碼:
- debug
#include "Common.xcconfig" APP_NAME = $(inherited)Debug
- Mamba
#include "Common.xcconfig" APP_NAME = $(inherited)Mamba
- release
```Swift #include "Common.xcconfig" APP_NAME = $(inherited)Release
上面利用#include
進(jìn)行導(dǎo)入依賴(lài)的Configuration
文件,并利用$(inherited)
來(lái)引用依賴(lài)的Configuration
文件中的變量。
Configuration
文件中的語(yǔ)法一般是SETTING_NAME = VALUE
,具體等式二邊設(shè)置的值可見(jiàn)蘋(píng)果官網(wǎng).
設(shè)置Configuration
點(diǎn)擊PROJECT
導(dǎo)航欄并選擇Info
會(huì)發(fā)現(xiàn)多了一個(gè)上文我們添加的名為Mamba
的Configuration
。
點(diǎn)擊左邊的小三角箭頭展開(kāi)每個(gè)Configuration
后可以設(shè)置項(xiàng)目的project
級(jí)別的Configuration File
和target
級(jí)別的Configuration File
,當(dāng)然也可以默認(rèn)不設(shè)置。分別設(shè)置三個(gè)Configuration
下的project
級(jí)別的Configuration File
為Base
,target
級(jí)別的Configuration File
則為對(duì)應(yīng)的Configuration File
,如下圖所示:
查看是否設(shè)置成功
點(diǎn)擊TARGETS
導(dǎo)航欄,選擇Build Settings
并選中All
和Levels
滑到最下面可看見(jiàn)APP_NAME
的值設(shè)置如下:
這里需要解釋一下幾個(gè)設(shè)置的級(jí)別:
-
Resolved
: 最后生效的值 -
Target
: 顯示在Target
級(jí)別生效的值,Target
級(jí)別的優(yōu)先級(jí)是高于Project
的,并且默認(rèn)繼承Project
設(shè)置的值。 -
Project
: 顯示在Project
級(jí)別生效的值,往常在Xcode
的General
設(shè)置的值就是這一級(jí)別的。 -
iOS Default
: 顯示iOS
默認(rèn)設(shè)置的值。
加上Configuration File
后優(yōu)先級(jí)順序從低到高如下:
- Platform defaults
- Project.xcconfig file
- Project file build settings
- Target .xcconfig file
- Target build settings
設(shè)置Info.plist
最后為了通過(guò)Configuration File
來(lái)控制APP
運(yùn)行時(shí)名字的顯示,需要在Info.plist
中鏈接Bundle display name
屬性(沒(méi)有的話(huà)需要新增)到我們上面設(shè)置的user-defined setting(APP_NAME)
上,為此修改Info.plist
中Bundle display name
的值為 $(APP_NAME)
。
測(cè)試是否生效
在Scheme
頁(yè)面分別選擇debug
,release
和mamba
三中不同的Configuration
環(huán)境運(yùn)行APP
成功的根據(jù)不同的Configtation
設(shè)置不同的項(xiàng)目運(yùn)行名字。
利用xconfig文件實(shí)現(xiàn)OC條件編譯
在開(kāi)發(fā)中經(jīng)常需要進(jìn)行條件編譯,在OC
中可以利用pch
文件配合宏來(lái)實(shí)現(xiàn),例如如下:
#ifdef DEBUG #define BaseURL @"192.168.1.1:8080/appname/api" #define PublicKEY @"QWE3R23WR09WURI220WR3TTY5ET3CR2X" #else #define BaseURL @"http://api.appname.com" #define PublicKEY @"32GDG4575UB5M97O7M2X32RFH53QWT43" #endif
通過(guò)在pch
文件中利用條件編譯定義不用的宏來(lái)實(shí)現(xiàn)項(xiàng)目的動(dòng)態(tài)切換配置,上述宏定義一般定義在.pch
中,通常.pch
文件中定義的宏都比較雜亂,希望能單獨(dú)放在一個(gè)獨(dú)立的文件中,可以通過(guò)新建一個(gè)頭文件env.h
, 把上述宏定義放到env.h
中,在需要使用的時(shí)候?qū)腩^文件即可,把環(huán)境參數(shù)單獨(dú)放在一個(gè)獨(dú)立的頭文件中,更加簡(jiǎn)潔,職能更加專(zhuān)一,也便于維護(hù)但是這種做法還不是最好的,因?yàn)檫€需要手動(dòng)導(dǎo)入頭文件,而且生產(chǎn)環(huán)境參數(shù)和開(kāi)發(fā)環(huán)境參數(shù)是放在同一個(gè)文件中而是不是獨(dú)立分開(kāi)的,要想獨(dú)立分開(kāi)并且使用時(shí)又不用導(dǎo)入頭文件可以通過(guò)Xcode
中的Configurations Setting Fil(.xcconfig)
來(lái)解決,這應(yīng)該是最優(yōu)的實(shí)現(xiàn)方式。
xconfig文件的設(shè)置
在上面的Debug.xconfig
和Mamba.xconfig
文件中分別加入如下代碼:
- Debug.xconfig
WEBSERVICE_URL = @"www.baidu.com"
- Mamba.xconfig
WEBSERVICE_URL = @"www.jd.com"
這樣只是自定義了一個(gè)Build Setting
變量,不能代碼里像使用宏那樣使用,Xcode
是支持利用GCC_PREPROCESSOR_DEFINITIONS
在定義宏的,在Common.xconfig
文件中加入如下代碼:
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) WEBSERVICE_URL='$(WEBSERVICE_URL)'
在TARGET
導(dǎo)航欄中Preprocessor Macros
即可看見(jiàn)我們定義的宏。
代碼使用
可以在代碼中直接使用定義的宏,當(dāng)切換Configuration
時(shí)則會(huì)根據(jù).xconfig
文件輸入不同的打印。
- (void)viewDidLoad { [super viewDidLoad]; NSLog(@"-----------%@-------------",WEBSERVICE_URL); }
使用#include
語(yǔ)法來(lái)包含其他配置文件,如#include "Common.xcconfig"
, 最好是放在文件的最后面,放在文件的開(kāi)頭也可以。Common.xconfig
中第一個(gè)鍵的配置必須有:GCC_PREPROCESSOR_DEFINITIONS = $(inherited)
,沒(méi)有Xcode
會(huì)報(bào)錯(cuò),暴露自定義鍵時(shí)的語(yǔ)法:宏名='$(key)'
,在代碼或其他地方使用宏名來(lái)引用,'$(key)'
:通過(guò)key
來(lái)指定每個(gè)模式下的對(duì)應(yīng)的自定義鍵的名字,通常將宏的名字和key
的名字保持一致, 注意 等號(hào)前后一定不能有空,Common.xconfig
中第一個(gè)key
是GCC_PREPROCESSOR_DEFINITIONS = $(inherited)
后面跟自定義的key
,注意在第一個(gè)key
后面跟上自己定義的key
的時(shí)候一定不要回車(chē)換行,敲一個(gè)空格,然后在同一行后面追加就行了,換行會(huì)編譯錯(cuò)誤, 不能換行,不能換行,不能換行!
Swift中條件編譯的實(shí)現(xiàn)
在Swift
中是不支持通過(guò)GCC_PREPROCESSOR_DEFINITIONS
來(lái)定義宏的,但是可以通過(guò)定義Custom Flags
進(jìn)行定義,這里介紹另外一種方法,還是通過(guò).xconfig文件
進(jìn)行獲取我們需要的宏。前面我們通過(guò)info.plist
獲取到了.xconfig
文件中自定義的變量,再次我們同樣通過(guò)info.plist
來(lái)獲取自定義的變量的值來(lái)當(dāng)做宏使用,首先在info.plist
中新建一個(gè)WEBSERVICE_URL
變量,并設(shè)置值為'$(WEBSERVICE_URL)'
,由于需要解析info.plist
中的變量,再次封裝一個(gè)config.swift
的類(lèi)用來(lái)解析:
import Foundation enum Config { static func stringValue(forKey key: String) -> String { guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String else { fatalError("Invalid value or undefined key") } return value } }
代碼使用
import UIKit class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() print(Config.stringValue(forKey:"WEBSERVICE_URL")) } }
相比較于OC
版本的是不能直接定義宏,需要通過(guò)在info.plist
定義后并通過(guò)方法取出值后才能使用,稍微麻煩了一點(diǎn)。
script的使用
上文我們已經(jīng)知道xconfig
文件的使用,其實(shí)在編譯之前不只是變量的自定義或者獲取項(xiàng)目的一些默認(rèn)參數(shù),還可以在獲取這些參數(shù)的基礎(chǔ)上,將這些參數(shù)作為script
腳本的變量來(lái)做一些更有意義的事情,Xcode
是支持在編譯之前鏈接script
腳本的。
script的初步認(rèn)識(shí)
腳本一般來(lái)說(shuō)就是可執(zhí)行的二進(jìn)制文件,下面先制作一個(gè)簡(jiǎn)單的腳本加深認(rèn)知(實(shí)例代碼采用Swift
),首先新建一個(gè)名為HelloXcode.swift
文件,加入如下代碼:
import Foundation @main enum MyScript { static func main() { print("Hello Xcode") } }
下面我們用終端來(lái)編譯上面的HelloXcode.swift
文件,cd
到文件所在的目錄執(zhí)行以下代碼:
xcrun --sdk macosx swiftc -parse-as-library HelloXcode.swift -o CompiledScript
利用macOS SDK
來(lái)編譯HelloXcode.swift
并輸出名為CompiledScript
的二進(jìn)制腳本文件,此時(shí)可以直接在當(dāng)前目錄利用./CompiledScript
執(zhí)行該腳本文件,會(huì)直接輸出打印HelloXcode
。為了在Xcode
編譯階段就能運(yùn)行腳本,我們需要將腳本插入到Xcode
的Build Phases
中,首先我們先新建一個(gè)Build Phases
如下所示:
Xcode
中的Build Phases
選項(xiàng)卡是Xcode build
項(xiàng)目的中心,Xcode
在編譯項(xiàng)目時(shí)其實(shí)幫我們做了如下幾件事情:
- 確定項(xiàng)目的一些依賴(lài)并編譯
- 編譯項(xiàng)目的代碼
- 鏈接上面編譯的依賴(lài)文件
- 復(fù)制資源文件例如圖片等到項(xiàng)目bundle中
這里我們是要在項(xiàng)目編譯開(kāi)始之前就運(yùn)行腳本,所以需要調(diào)整新增加的Build Phases
的順序,直接拖到Denpencies
下面,如下圖所示:
點(diǎn)擊剛剛新加的Build Phases
可以重命名,展開(kāi)后加入如下代碼:
下面的Input Files
可以理解為腳本的變量,這里將HelloXcode.swift
相對(duì)工程文件所在的路$SCRIPT_INPUT_FILE_0
進(jìn)行引用,$(SRCROOT)
代表工程文件所在的目錄,運(yùn)行項(xiàng)目在build log
(不是打印臺(tái))會(huì)看見(jiàn)如下輸出:
script的實(shí)際運(yùn)用
利用script
來(lái)實(shí)現(xiàn)每當(dāng)build的時(shí)候改變 Info.plist
中Bundle version
或者Bundle version string (short)
的值,新建一個(gè)IncBuildNumber.swift
文件,加入如下代碼:
import Foundation @main enum IncBuildNumber { static func main() { guard let infoFile = ProcessInfo.processInfo .environment["INFOPLIST_FILE"] else { return } guard let projectDir = ProcessInfo.processInfo.environment["SRCROOT"] else { return } if var dict = NSDictionary(contentsOfFile: projectDir + "/" + infoFile) as? [String: Any] { guard let currentVersionString = dict["CFBundleShortVersionString"] as? String, let currentBuildNumberString = dict["CFBundleVersion"] as? String, let currentBuildNumber = Int(currentBuildNumberString) else { return } dict["CFBundleVersion"] = "\(currentBuildNumber + 1)" if ProcessInfo.processInfo.environment["CONFIGURATION"] == "Release" { var versionComponents = currentVersionString .components(separatedBy: ".") let lastComponent = (Int(versionComponents.last ?? "1") ?? 1) versionComponents[versionComponents.endIndex - 1] = "\(lastComponent + 1)" dict["CFBundleShortVersionString"] = versionComponents .joined(separator: ".") } (dict as NSDictionary).write( toFile: projectDir + "/" + infoFile, atomically: true) } } }
當(dāng)Xcode
在執(zhí)行run script phase
時(shí)會(huì)通過(guò)環(huán)境變量environment variables
來(lái)共享build settings
,可以將環(huán)境變量在這里理解為全局變量,這里通過(guò)環(huán)境變量拿到了info.plist
中的CFBundleShortVersionString
和CFBundleVersion
變量,并根據(jù)CONFIGURATION
配置的是Release
還是Debug
來(lái)修改對(duì)應(yīng)的BundleVersion
,至此每當(dāng)build時(shí)
都會(huì)改變相應(yīng)的BuildVersion
值。
總結(jié)
原文鏈接:https://www.jianshu.com/p/81bbe0b0761a
相關(guān)推薦
- 2022-04-01 win10應(yīng)用程序開(kāi)機(jī)自動(dòng)啟動(dòng)設(shè)置
- 2022-09-14 Python詳解如何動(dòng)態(tài)給對(duì)象增加屬性和方法_python
- 2022-08-19 淺談tensorflow與pytorch的相互轉(zhuǎn)換_python
- 2023-02-25 如何用redis?setNX命令來(lái)加鎖_Redis
- 2022-11-03 React狀態(tài)管理器Rematch的使用詳解_React
- 2023-07-04 spring中Bean創(chuàng)建完后打印語(yǔ)句的兩種方法(Bean的生命周期)
- 2022-11-02 python調(diào)用subprocess模塊實(shí)現(xiàn)命令行操作控制SVN的方法_python
- 2023-11-17 Linux CentOS如何修改root用戶(hù)密碼
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支