[iOS]從零開始寫個自動打包IPA腳本
新項目這邊每次版本移交,
給測試都是直接xcode挨個拿手機安裝的流程。
給測試都是直接xcode挨個拿手機安裝的流程。
一次兩次還好,天天這麼搞,而且每次一大波手機扔過來,瞬間覺得,自己好像是個疫苗注射員似的,順時給每個手機打一針。
biu!
NEXT ...
BIU!
NEXT ...
BIU!
NEXT ...
...
😢
...
於是..我崩潰了..
biu!
NEXT ...
BIU!
NEXT ...
BIU!
NEXT ...
...
😢
...
於是..我崩潰了..
好吧,
是在是看不下去了,於是準備寫個腳本以後自己打包,測試直接下載就好了。
是在是看不下去了,於是準備寫個腳本以後自己打包,測試直接下載就好了。
思路
那麼怎麼實現這麼一個事情呢?
其實上一家公司,做個類似的東西,雖然不是我做的,但是大體流程還是知道的
- 寫個bash腳本,執行自動打包iOS版本,到指定的目錄(有條件的公司,可以自己搭個小服務器,這樣誰都可以隨時隨地的打包)
- 將打包好的文件上傳到fir.im(當然上傳到自己公司的服務器或者任何地方都行,只是fir.im我一直用,覺得比較方便)
- 開發一個內部使用的類似APPStore,上面放著自己公司的所有APP,每次有更新的時候,測試童鞋直接通過這個自己下載新APP就可以了
開始做
準備背景知識
其實當我們Xcode點擊了構建或者運行comand + R之後,Xcode自己執行的命令是xcodebuild這條命令。
然後,編譯好之後,怎么生產ipa包?
用xcrun命令
然後,編譯好之後,怎么生產ipa包?
用xcrun命令
話不多說,先上手:
打開終端,cd到你的工程位置,然後先試一下xcodebuild命令,
打開終端,cd到你的工程位置,然後先試一下xcodebuild命令,
//xcrun
chengpoleness@polen xcodebuild_iPA $ xcrun --version
xcrun version 29.
//xcodebuild
chengpoleness@polen ios (develop) $ xcodebuild -version
Xcode 7.3
Build version 7D175
看上去很簡單,大概了解了一下,就開始用:
chengpoleness@polen ios (develop) $ xcodebuild
2016-05-02 13:05:04.623 xcodebuild[1015:16272] [MT] PluginLoading: Required plug-in compatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/XcodeColors.xcplugin' not present in DVTPlugInCompatibilityUUIDs
2016-05-02 13:05:04.625 xcodebuild[1015:16272] [MT] PluginLoading: Required plug-in compatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/OMColorSense.xcplugin' not present in DVTPlugInCompatibilityUUIDs
=== BUILD TARGET xxx OF PROJECT xxx WITH THE DEFAULT CONFIGURATION (Release) ===
Check dependencies
Write auxiliary files
write-file /Users/chengpoleness/Documents/code/xxx/ios/build/xxx.build/Release-iphoneos/xxx.build/xxx.hmap
write-file /Users/chengpoleness/Documents/code/xxx/ios/build/xxx.build/Release-iphoneos/xxx.build/xxx-own-target-headers.hmap
write-file /Users/chengpoleness/Documents/code/xxx/ios/build/xxx.build/Release-iphoneos/xxx.build/Script-492B764475E022A63FB67F55.sh
接著滿屏的快速滾動,可以看到鐺依次在編輯各個文件,登錄大概十幾秒閃屏,然後失敗了!!!💔
ld: library not found for -lPods
clang: error: linker command failed with exit code 1 (use -v to see invocation)
** BUILD FAILED **
The following build commands failed:
Ld build/xxx.build/Release-iphoneos/xxx.build/Objects-normal/armv7/xxx normal armv7
Ld build/xxx.build/Release-iphoneos/xxx.build/Objects-normal/arm64/xxx normal arm64
(2 failures)
這個就是找不到pods了,
為什麼會這樣?
自己直接用Xcode親自產品檔案了一遍,okey啊,沒出現任何問題!
那是為什麼呢?
為什麼會這樣?
自己直接用Xcode親自產品檔案了一遍,okey啊,沒出現任何問題!
那是為什麼呢?
各種goole,stackoveflow,但是基本說的是不要只打開項目,而是打開工作區,其實這個是針對直接用xcode進行編譯或者打包的時候,出現問題的解決方案,
然後嘗試了其他解決方案,如ONLY_ACTIVE_ARCHS = NO等,都無效。
最終在下面這篇文章裡,找到了靈感:
iOS集成測試與Appium
然後嘗試了其他解決方案,如ONLY_ACTIVE_ARCHS = NO等,都無效。
最終在下面這篇文章裡,找到了靈感:
iOS集成測試與Appium
其實解決方案很簡單,執行xcodebuild需要指定你所需要對應的工作空間和方案
所以命令如下:
xcodebuild -workspace /Users/chengpoleness/Documents/code/xxx/ios/xxx.xcworkspace -scheme xxx
執行前,先查看下列列表,這個可以知道xcodebuild命令下對應的參數需要填寫的內容
chengpoleness@polen ios (develop) $ xcodebuild -list
2016-05-02 15:24:26.656 xcodebuild[16535:154176] [MT] PluginLoading: Required plug-in compatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/XcodeColors.xcplugin' not present in DVTPlugInCompatibilityUUIDs
2016-05-02 15:24:26.661 xcodebuild[16535:154176] [MT] PluginLoading: Required plug-in compatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/OMColorSense.xcplugin' not present in DVTPlugInCompatibilityUUIDs
Information about project "xxx":
Targets:
xxx
xxxTests
Build Configurations:
Debug
Release
If no build configuration is specified and -scheme is not passed then "Release" is used.
Schemes:
xxx
然後,成功了,如下:
chengpoleness@polen ios (develop) $ xcodebuild -workspace /Users/chengpoleness/Documents/code/xxx/ios/xxx.xcworkspace -scheme xxx
2016-05-02 15:26:29.627 xcodebuild[16552:156501] [MT] PluginLoading: Required plug-in compatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/XcodeColors.xcplugin' not present in DVTPlugInCompatibilityUUIDs
2016-05-02 15:26:29.629 xcodebuild[16552:156501] [MT] PluginLoading: Required plug-in compatibility UUID ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C for plug-in at path '~/Library/Application Support/Developer/Shared/Xcode/Plug-ins/OMColorSense.xcplugin' not present in DVTPlugInCompatibilityUUIDs
=== BUILD TARGET Pods OF PROJECT Pods WITH CONFIGURATION Debug ===
Check dependencies
=== BUILD TARGET xxx OF PROJECT xxx WITH CONFIGURATION Debug ===
Check dependencies
PhaseScriptExecution Check\ Pods\ Manifest.lock /Users/chengpoleness/Library/Developer/Xcode/DerivedData/xxx-djwnuzytnxhekrhjhypwxdqrxtet/Build/Intermediates/xxx.build/Debug-iphoneos/xxx.build/Script-492B764475E022A63FB67F55.sh
...
...
...
Validate /Users/chengpoleness/Library/Developer/Xcode/DerivedData/xxx-djwnuzytnxhekrhjhypwxdqrxtet/Build/Products/Debug-iphoneos/xxx.app
cd /Users/chengpoleness/Documents/code/xxx/ios
export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/Users/chengpoleness/.rvm/gems/ruby-2.0.0-p598/bin:/Users/chengpoleness/.rvm/gems/ruby-2.0.0-p598@global/bin:/Users/chengpoleness/.rvm/rubies/ruby-2.0.0-p598/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Users/chengpoleness/.rvm/bin:/usr/local/marven/apache-maven-3.3.9/bin:/Users/chengpoleness/Library/Android/sdk/platform-tools:/Users/chengpoleness/Library/Android/sdk/tools"
export PRODUCT_TYPE=com.apple.product-type.application
builtin-validationUtility /Users/chengpoleness/Library/Developer/Xcode/DerivedData/xxx-djwnuzytnxhekrhjhypwxdqrxtet/Build/Products/Debug-iphoneos/xxx.app
** BUILD SUCCEEDED **
寫腳本
(挑重點地說)大致流程就是:
2.1先git指令,拉到最新的分支
這個很簡單,沒什麼好說的了。
git clean -df
git reset --hard
git fetch
git checkout $BRANCHNAME
git pull --rebase origin $BRANCHNAME
echo "即将打包的分支log如下:"
git log -5
pod update --verbose --no-repo-update
不過這幾個指令,帶有刪除性,比如git clean -df,git reset這種,所以用的時候要謹慎,如果你把腳本放在自己的工程目錄下,就可能一不小心腳本就被刪除掉了(親歷者說...😢)
所以最終版本改成了
# git update
git checkout $BRANCHNAME
if [ $? -ne 0 ]; then
exit 1
fi
git pull
#pod update --verbose --no-repo-update
if [ $? -ne 0 ]; then
exit 1
fi
只保留了checkout和pull,pod沒有用,是因為我們這邊除了googleMap其他全是自造輪子,所以...並不需要😄。
2.2 xcodebuild進行編譯
xcodebuild \
-workspace $SORCEPATH/xxx.xcworkspace \
-scheme $SCHEMENAMEPLQ \
-configuration Debug \
CODE_SIGN_IDENTITY="iPhone Developer: xxxx(T8TXPM5FC7)" \
PROVISIONING_PROFILE="5110ff0a-b845-475e-a4xxxxxxxxxx" \
clean \
build \
-derivedDataPath $IPAPATH/$BRANCHNAME/$DATE
備註:
我這邊因為是研發階段,所以打包是調試包,如果是要打發包,配置中修改成發布,以及其他對應的修改下就行。
我這邊因為是研發階段,所以打包是調試包,如果是要打發包,配置中修改成發布,以及其他對應的修改下就行。
2.3成功後xcrun打包成ipa包
xcrun -sdk iphoneos PackageApplication \
-v $IPAPATH/Build/Products/Debug-iphoneos/$SCHEMENAME.app \
-o $IPAPATH/$IPANAME
最終腳本如下:

屏幕快照稿04下午8.20.47.png
打包的文件命名我是按照時間來命名的,這樣也方便找,尤其應對於可能分分鐘就要突然來打包的測試團隊。
生成ipa包,上傳到fir.im
3.1 fir比較好,有命令可以直接上傳,但是需先安裝fir-cli
安裝過程如下:
chengpoleness@polen xcodebuild_iPA $ gem install fir-cli
Fetching: chunky_png-1.3.5.gem (100%)
Successfully installed chunky_png-1.3.5
Fetching: rqrcode-0.10.1.gem (100%)
Successfully installed rqrcode-0.10.1
...
...
______________ ________ ____
/ ____/ _/ __ \ / ____/ / / _/
/ /_ / // /_/ /_____/ / / / / /
/ __/ _/ // _, _/_____/ /___/ /____/ /
/_/ /___/_/ |_| \____/_____/___/
...
...
Installing ri documentation for thor-0.19.1
Parsing documentation for fir-cli-1.4.9
unable to convert "\xCE" from ASCII-8BIT to UTF-8 for lib/fir/util/parser/bin/pngcrush, skipping
Installing ri documentation for fir-cli-1.4.9
13 gems installed
安裝如果遇到問題,可以到這裡查看原因
3.2使用fir指令,上傳我們的ipa包
非常簡單
fir login -T c525718a775b954882xxxxxxxx # fir.im token
fir publish $IPAPATH/Develop/xxx.ipa
3.3擴展
另外,也可以直接用fir指令,進行打包或者編譯,這個具體也沒研究,其實就對對xcodebuild做了個封裝,本質上還是執行那些指令,這裡不再詳述了。
chengpoleness@polen xcodebuild_iPA $ fir
Commands:
fir build_apk BUILD_DIR # Build Android app (alias: `ba`).
fir build_ipa BUILD_DIR [options] [settings] # Build iOS app (alias: `bi`).
fir help # Describe available commands or one specific command (aliase...
fir info APP_FILE_PATH # Show iOS/Android app info, support ipa/apk file (aliases: `...
fir login # Login fir.im (aliases: `l`).
fir mapping MAPPING_FILE_PATH # Upload app mapping file to BugHD.com (aliases: `m`).
fir me # Show current user info if user is logined.
fir publish APP_FILE_PATH # Publish iOS/Android app to fir.im, support ipa/apk file (al...
fir upgrade # Upgrade fir-cli and quit (aliases: `u`).
fir version # Show fir-cli version number and quit (aliases: `v`).
Options:
-T, [--token=TOKEN] # User's API Token at fir.im
-L, [--logfile=LOGFILE] # Path to writable logfile
-V, [--verbose], [--no-verbose] # Show verbose
# Default: true
-q, [--quiet], [--no-quiet] # Silence commands
-h, [--help], [--no-help] # Show this help message and quit
測試一下
4.1執行.sh文件
chengpoleness@polen xcodebuild_iPA $ sh xcodebuild.sh
要是有比較懶的同學,直接把.sh文件,拖到命令行里面也行
4.2過程與結果
編譯過程的輸出信息太多,刪減了下,大概看一下流程:
chengpoleness@polen xcodebuild_iPA $ sh xcodebuild.sh
HEAD is now at 30865e1 version 3.5.0, build 29
Already on 'develop'
...
...
=== CLEAN TARGET Pods OF PROJECT Pods WITH CONFIGURATION Debug ===
=== CLEAN TARGET xxx OF PROJECT xxx WITH CONFIGURATION Debug ===
...
...
** BUILD SUCCEEDED **
/* '以上是编译成功,接下来打包ipa' */
...
Packaging application: '/Users/chengpoleness/Documents/code/xxx/buildiPA/develop/20160502_1801/Build/Products/Debug-iphoneos/xxx.app'
Arguments: output=/Users/chengpoleness/Documents/code/xxx/buildiPA/develop/20160502_1801/1xxx_20160502_1801.ipa verbose=1
Environment variables:
_system_name = OSX
...
### Checking original app
...
Done checking the original app
...
adding: Payload/xxxx.app/xxxViewGpsOverlay.nib (in=4889) (out=2502) (deflated 49%)
total bytes=53793797, compressed=23588024 -> 56% savings
]
Results at 'xxxxxxxx/Documents/code/xxx/buildiPA/develop/20160502_1801/xxx_20160502_1801.ipa'
/* '以上是打包成功,接下来是上传fir.im' */
I, [2016-05-02T18:03:56.614082 #47670] INFO -- : Login succeed, current user's email: xxxx
I, [2016-05-02T18:03:56.614215 #47670] INFO -- :
I, [2016-05-02T18:03:57.766434 #47674] INFO -- : Publishing app via chenglong<chenglong@squarevalleytech.com>.......
I, [2016-05-02T18:03:57.766570 #47674] INFO -- : ✈ -------------------------------------------- ✈
I, [2016-05-02T18:03:59.813783 #47674] INFO -- : Fetching com.osmgolf.xxx@fir.im uploading info......
I, [2016-05-02T18:03:59.813868 #47674] INFO -- : Uploading app: xxx-3.5.0(Build 29)
I, [2016-05-02T18:04:00.562896 #47674] INFO -- : Uploading app icon......
I, [2016-05-02T18:04:00.563029 #47674] INFO -- : Converting app's icon......
I, [2016-05-02T18:04:01.715439 #47674] INFO -- : Uploading app binary......
I, [2016-05-02T18:04:16.091891 #47674] INFO -- : Updating devices info......
I, [2016-05-02T18:04:16.244710 #47674] INFO -- : Updating app info......
I, [2016-05-02T18:04:16.514628 #47674] INFO -- : ✈ -------------------------------------------- ✈
I, [2016-05-02T18:04:16.514862 #47674] INFO -- : Fetch app info from fir.im
I, [2016-05-02T18:04:17.019304 #47674] INFO -- : Published succeed: http://fir.im/xxxx
I, [2016-05-02T18:04:17.019409 #47674] INFO -- :
恭喜!!!上传fir.im成功!
OK,測試通過。
5.開發內部版“AppStore”
這個就這裡
不詳了了,因為太簡單。寫個tableview,把自己的app的鏈接放上去,每次有新版本了,通過這個App就可以下載,之前做過截圖如下:
不詳了了,因為太簡單。寫個tableview,把自己的app的鏈接放上去,每次有新版本了,通過這個App就可以下載,之前做過截圖如下:

至此,自動打包IPA的相關工作開發完成,可以投入使用了...
6擴展-Debug / Release
6.1背景:
目前的腳本打包都是默認打包包的,但是如果我打版包包怎麼辦?如果我調試包不同網絡域名想切換怎麼辦?
6.2調試/釋放
Okey,我們依次來解決這些問題:
其實很簡單,xcodebuild指令有個個配置,我們使用這個決定是打Debug還是發布
其實很簡單,xcodebuild指令有個個配置,我們使用這個決定是打Debug還是發布
那個打出來的調試和發布有什麼區別呢?
這個相當於編譯的時候是按照調試模式或者發布模式來打包,從編譯角度來說,區別挺大的,比如Debug會在某些堆棧環節預留更多的空間(我之前就遇到過同樣的代碼,但是Debug沒問題,發布會有bug的情況,如果不知道這個的話,很難查出來或者理解這些bug,具體細節有空專門細說吧,這裡
不詳了了)所以,解決方案:定義一個CONGRUATION,根據傳入的參數,決定打Debug還是打發行
這個相當於編譯的時候是按照調試模式或者發布模式來打包,從編譯角度來說,區別挺大的,比如Debug會在某些堆棧環節預留更多的空間(我之前就遇到過同樣的代碼,但是Debug沒問題,發布會有bug的情況,如果不知道這個的話,很難查出來或者理解這些bug,具體細節有空專門細說吧,這裡
不詳了了)所以,解決方案:定義一個CONGRUATION,根據傳入的參數,決定打Debug還是打發行
# $1表示传入的第一个参数,启动脚本传入Debug或者Release就可以
CONGRUATION=$1
# 在xcodebuild和xrun的地方都使用$CONGRUATION即可
xcodebuild \
-workspace $SOURCEPATH/xxx.xcworkspace \
-scheme $SCHEMENAME \
-configuration $CONGRUATION \
-derivedDataPath $IPAPATH \
xcrun -sdk iphoneos PackageApplication \
-v $IPAPATH/Build/Products/$CONGRUATION-iphoneos/$SCHEMENAME.app \
-o $IPAPATH/$IPANAME
6.2不同的主機域名切換
這個其實解決方案很多:
方案A:
代碼裡寫個調試頁面,裡面設置可以切換不同的域名即可,
以前公司的APP就是這麼做的,因為我們測量環境非常多,比如:
QA01,QA02,QA03 ... QA10 ...
所以直接在APP裡就可以進行切換不同域名。
以前公司的APP就是這麼做的,因為我們測量環境非常多,比如:
QA01,QA02,QA03 ... QA10 ...
所以直接在APP裡就可以進行切換不同域名。
polen提醒:
1>切換後一定要讓用戶退出,避免發生不必要的邏輯混亂
2>需要設置個宏開關,確保發布版本用戶100%走線上域名,Debug版本可以隨意加各種邏輯
判斷。務必避免如果代碼不建壯,發生線上用戶誤入測試環境的悲劇(當然,這個肯定屬於bug了,嚴格是不應該發生的了,但是難免意外,沒有東西是100%絕對的,這裡只是提醒.. 。)
方案B:
如果你們的測試環境沒那麼多,可能就2-3個,可以在打包的時候,通過腳本決定用哪個環境,具體怎麼做:
CTO說怎麼樣能讓腳本給代碼傳個值進入,然後代碼在編譯的時候根據這個值決定用哪個測試環境。
思考了很多方法,包括使用腳本去改代(硬改,用sed進入.m文件修改,但這樣太危險,而且很不友好)
後來學習到使用PREPROCESSOR_DEFINITIONS,就是xcode在打包前會先從一個配置中讀取一些配置信息,這些在xcode-building裡面可以直接設置,自然也可以通過xcodebuild指定穿進去,傳入的就是GCC_PREPROCESSOR_DEFINITIONS
所以就是:
設定一個SERVER_TYPE宏定義,如果#ifdef SERVER_TYPE的話,使用傳入的參數,否則使用代碼自定義的邏輯
後來學習到使用PREPROCESSOR_DEFINITIONS,就是xcode在打包前會先從一個配置中讀取一些配置信息,這些在xcode-building裡面可以直接設置,自然也可以通過xcodebuild指定穿進去,傳入的就是GCC_PREPROCESSOR_DEFINITIONS
所以就是:
設定一個SERVER_TYPE宏定義,如果#ifdef SERVER_TYPE的話,使用傳入的參數,否則使用代碼自定義的邏輯
# $serverType是启动脚本传入的参数值
if [ $CONGRUATION = Debug ]; then
PREPROCESSOR_DEFINITIONS="COCOAPODS=1 SERVER_TYPE=$serverType DEBUG=1"
else
PREPROCESSOR_DEFINITIONS="COCOAPODS=1 SERVER_TYPE=$serverType"
fi
xcodebuild \
-workspace $SOURCEPATH/xxx.xcworkspace \
-scheme $SCHEMENAME \
GCC_PREPROCESSOR_DEFINITIONS="$PREPROCESSOR_DEFINITIONS" \
-configuration $CONGRUATION \
-derivedDataPath $IPAPATH \
具體代碼中如何使用:
//HOST_TYPE决定最终的域名
#ifdef SERVER_TYPE
#define HOST_TYPE SERVER_TYPE
#else
#ifdef DEBUG
#define HOST_TYPE HOST_TYPE_CANARY
#else
#define HOST_TYPE HOST_TYPE_PRODUCTION
#endif
#endif
okey,以上就解決了Debug和Release相關的問題
遇到一些疑難雜症
br>
7.1 SDK不匹配導致編譯失敗
7.1.1背景
下午(2016.8.2)正在忙,突然測試同學跑過來,大喊:“不好了,不好了...”
我以為要說颱風來了,(做好了可以下班的準備..😄)
結果,來了句:
“打包腳本失敗了,失敗了!!”
我擦,啥情況,用了幾個月了好好的,突然就失敗了...
我以為要說颱風來了,(做好了可以下班的準備..😄)
結果,來了句:
“打包腳本失敗了,失敗了!!”
我擦,啥情況,用了幾個月了好好的,突然就失敗了...
7.1.1原因
後來確認下來是這麼個情況,iOS10發布了,之前一直拖延,最近不是很忙了,於是有同事下載了xcode8-beta3和iOS10的安裝配置文件。
然而iOS10的SDK更新,導致我們有個代碼邏輯無法實現,於是修了一下,然後繼續交給測試同學打包測試,結果就打包失敗了,我看了下失敗原因:
然而iOS10的SDK更新,導致我們有個代碼邏輯無法實現,於是修了一下,然後繼續交給測試同學打包測試,結果就打包失敗了,我看了下失敗原因:
error:
property 'firstAnchor' not found on object of type 'NSLayoutConstraint *'
object_setIvar(constraint.firstAnchor, referenceItem, view);

屏幕快照2016-08-02下午5.44.54.png
然後查了下代碼,代碼邏輯應該是沒問題的。而找不到這個屬性是因為這個代碼使用的iOS10的SDK,用舊版本自然是找不到,然後問了下測試,直接用xcode編譯可以那麼
分析下來就應該是路徑的問題了,
去她位於上看了下,(因為我自己沒裝xcode8,所以自己沒法調試)一看發現問題了,她目前是兩個xcode都在,但一個叫Xcode,一個叫Xcode-beta,腳本跑起來的時候,SDK肯定還跑得是以前的xcode的啊,自然編不過,於是改了一下名字,把原來的Xcode改為Xcode7,新的Xcode-beta改為Xcode。
然後,問題解決!😄
如果後續腳本有擴展,我會繼續更新此文。
注意:有些地方應該是iOS,而非ios,懶得改了,就這樣吧,嗯。
沒有留言:
張貼留言