指令集
通常會把CPU的擴展指令集稱為”CPU的指令集”(因為基本的,類似加減的指令似乎是必須被CPU所支持的指令)。每款CPU在設計時就規定了一系列與其硬件電路相配合的指令集。
arm處理器
Arm處理器,因為其低功耗和小尺寸而聞名,幾乎所有的手機處理器都基于arm,其在嵌入式系統中的應用非常廣泛,它的性能在同等功耗產品中也很出色。想了解ARM指令集的請點擊這里。
iPhone指令集
蘋果A7處理器支持兩個不同的指令集:32位ARM指令集(armv6|armv7|armv7s)和64位ARM指令集(arm64),i386|x86_64 是Mac處理器的指令集,i386是針對intel通用微處理器32架構的。x86_64是針對x86架構的64位處理器。當使用iOS模擬器的時候會遇到i386|x86_64,iOS模擬器沒有arm指令集。
xcode中設置指令集
主要有以下三個:Architectures、Valid Architectures、Build Active Architecture Only
Architectures
該編譯選項指定了工程將被編譯成支持哪些指令集,支持指令集是通過編譯生成對應的二進制數據包實現的,如果支持的指令集數目有多個,就會編譯出包含多個指令集代碼的數據包,造成最終編譯的包很大。
Valid Architectures
該編譯項指定可能支持的指令集,該列表和Architectures列表的交集,將是Xcode最終生成二進制包所支持的指令集。
比如,Valid Architectures設置的支持arm指令集版本有:armv7/armv7s/arm64,對應的Architectures設置的支持arm指令集版本有:armv7s,這時Xcode只會生成一個armv7s指令集的二進制包。
Build Active Architecture Only
該編譯項用于設置是否只編譯當前使用的設備對應的arm指令集。
當該選項設置成YES時,你連上一個armv7指令集的設備,就算你的Valid Architectures和Architectures都設置成armv7/armv7s/arm64,還是依然只會生成一個armv7指令集的二進制包。
通常情況下,開發測試都是在 Debug模式下,所以該編譯選項在Debug模式都設成YES,這樣在測試時只編譯一份指令集,有效提高開發效率。
Release模式為發布模式,需要支持各種設備指令集,所以設置為NO。
制作framework
xcode版本: 8.3.2,Mac OS版本:10.12
創建一個新工程,在choose template頁面選擇Cocoa Touch Framework,如下:
在工程中添加或新建類,然后編譯生成framework,下面是選擇不同設備或模擬器時所生成framework:
1、debug環境下
設備:arm64(測試機型有限:6P、5、7)
模擬器:iPhone7-Plus:x86_64、iPhone4s:i386
2、release環境下
設備:armv7、arm64
模擬器:i386、x86_64
以上所生成的framework均不包含armv7s,在 Building Setting 中設置一下 Architectures,在原有基礎上添加一行 armv7s ,如下:
有一點需要注意的是,如果制作 framework 時采用的是 Objective-C,則需要將希望外部使用的.h文件添加到 public 中,如下:
查看生成的framework
右鍵 Products 目錄下的 framework 文件 “show in finder”,會看到 finder 下存在模擬器和真機生成的 framework,如下:
合并framework
制作framework的過程中經常會遇到編譯出來的framework只能被真機使用或者只能被模擬器使用的情況。造成這個問題的原因是由于在編譯時選擇的目標設備不同的情況下編譯出來framework體系結構不同,選擇真機進行編輯時會編譯產生armv7、armv7s、arm64下的庫文件,而選擇模擬器會產生i386、x86_64下的庫文件。但導入過程中時希望支持所有架構,所以需要將 framework 合并
查看 framework 所支持架構
lipo -info frame_name.framework/framework_name
framework 合并命令:
-create后為要合并在一起的兩個framework,-output后面為合并后的framework名稱及路徑
lipo –create /Debug-iphoneos/Someframework.framwork/Someframework Debug-iphonesimulator/Someframework.framwork/Someframework –output Someframework
合并命令只是合并 framework 下的可執行文件,因此需要用合并后的 exec 文件替換真機或模擬器 framework 中的 exec 文件。
framework合并腳本:
在xcode中添加腳本后會自動將生成的模擬器與設備的framework合并,比使用合并命令手動合并更簡單,添加步驟如下:
在 Build Phases 中點擊左上角 + ,選擇 New Run Script Phase 添加一項
會在頁面底部添加一項 Run Script ,如下:
將下面的腳本代碼粘貼至提示“Type a script or drag…”輸入框內
if [ "${ACTION}" = "build" ]
then
INSTALL_DIR=${SRCROOT}/Products/${PROJECT_NAME}.framework
DEVICE_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphoneos/${PROJECT_NAME}.framework
SIMULATOR_DIR=${BUILD_ROOT}/${CONFIGURATION}-iphonesimulator/${PROJECT_NAME}.framework
if [ -d "${INSTALL_DIR}" ]
then
rm -rf "${INSTALL_DIR}"
fi
mkdir -p "${INSTALL_DIR}"
cp -R "${DEVICE_DIR}/" "${INSTALL_DIR}/"
#ditto "${DEVICE_DIR}/Headers" "${INSTALL_DIR}/Headers"
lipo -create "${DEVICE_DIR}/${PROJECT_NAME}" "${SIMULATOR_DIR}/${PROJECT_NAME}" -output "${INSTALL_DIR}/${PROJECT_NAME}"
open "${DEVICE_DIR}"
open "${SRCROOT}/Products"
fi
測試一下腳本,在release環境下分別選擇真機和模擬器運行,運行成功后會自動打開Finder并定位到工程根目錄下的Products:
此時查看Products下的framework所支持的指令集:
動態庫、靜態庫與Framework
庫(Library)說白了就是一段編譯好的二進制代碼,加上頭文件就可以供別人使用。
什么時候我們會用到庫呢?一種情況是某些代碼需要給別人使用,但是我們不希望別人看到源碼,就需要以庫的形式進行封裝,只暴露出頭文件。另外一種情況是,對于某些不會進行大的改動的代碼,我們想減少編譯的時間,就可以把它打包成庫,因為庫是已經編譯好的二進制了,編譯的時候只需要 Link 一下,不會浪費編譯時間。
上面提到庫在使用的時候需要 Link,Link 的方式有兩種,靜態和動態,于是便產生了靜態庫和動態庫。
靜態庫
靜態庫即靜態鏈接庫(Windows 下的 .lib,Linux 和 Mac 下的 .a)。之所以叫做靜態,是因為靜態庫在編譯的時候會被直接拷貝一份,復制到目標程序里,這段代碼在目標程序里就不會再改變了。
靜態庫的好處很明顯,編譯完成之后,庫文件實際上就沒有作用了。目標程序沒有外部依賴,直接就可以運行。當然其缺點也很明顯,就是會使用目標程序的體積增大。
動態庫
動態庫即動態鏈接庫(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib/.tbd)。與靜態庫相反,動態庫在編譯時并不會被拷貝到目標程序中,目標程序中只會存儲指向動態庫的引用。等到程序運行時,動態庫才會被真正加載進來。
動態庫的優點是,不需要拷貝到目標程序中,不會影響目標程序的體積,而且同一份庫可以被多個程序使用(因為這個原因,動態庫也被稱作共享庫)。同時,編譯時才載入的特性,也可以讓我們隨時對庫進行替換,而不需要重新編譯代碼。動態庫帶來的問題主要是,動態載入會帶來一部分性能損失,使用動態庫也會使得程序依賴于外部環境。如果環境缺少動態庫或者庫的版本不正確,就會導致程序無法運行(Linux 下喜聞樂見的 lib not found 錯誤)。
iOS Framework
除了上面提到的 .a 和 .dylib/.tbd 之外,Mac OS/iOS 平臺還可以使用 Framework。Framework 實際上是一種打包方式,將庫的二進制文件,頭文件和有關的資源文件打包到一起,方便管理和分發。
在 iOS 8 之前,iOS 平臺不支持使用動態 Framework,開發者可以使用的 Framework 只有蘋果自家的 UIKit.Framework,Foundation.Framework 等。這種限制可能是出于安全的考慮(見這里的討論)。換一個角度講,因為 iOS 應用都是運行在沙盒當中,不同的程序之間不能共享代碼,同時動態下載代碼又是被蘋果明令禁止的,沒辦法發揮出動態庫的優勢,實際上動態庫也就沒有存在的必要了。
由于上面提到的限制,開發者想要在 iOS 平臺共享代碼,唯一的選擇就是打包成靜態庫 .a 文件,同時附上頭文件(例如微信的SDK)。但是這樣的打包方式不夠方便,使用時也比較麻煩,大家還是希望共享代碼都能能像 Framework 一樣,直接扔到工程里就可以用。于是人們想出了各種奇技淫巧去讓 Xcode Build 出 iOS 可以使用的 Framework,具體做法參考這里和這里,這種方法產生的 Framework 還有 “偽”(Fake) Framework 和 “真”(Real) Framework 的區別。
iOS 8/Xcode 6 推出之后,iOS 平臺添加了動態庫的支持,同時 Xcode 6 也原生自帶了 Framework 支持(動態和靜態都可以),上面提到的的奇技淫巧也就沒有必要了(新的做法參考這里)。為什么 iOS 8 要添加動態庫的支持?唯一的理由大概就是 Extension 的出現。Extension 和 App 是兩個分開的可執行文件,同時需要共享代碼,這種情況下動態庫的支持就是必不可少的了。但是這種動態 Framework 和系統的 UIKit.Framework 還是有很大區別。系統的 Framework 不需要拷貝到目標程序中,我們自己做出來的 Framework 哪怕是動態的,最后也還是要拷貝到 App 中(App 和 Extension 的 Bundle 是共享的),因此蘋果又把這種 Framework 稱為 Embedded Framework。