14年6月3日蘋果發(fā)布Swift以來,這門語言以讓人驚訝的速度在成長,越來越多的開發(fā)者關(guān)注學(xué)習(xí),很多App和開源庫也在從Objective-C遷移到Swift上。
Swift語法確實(shí)更新進(jìn)、更漂亮,而在實(shí)際開發(fā)過程中,由于Objective-C更貼近底層,可以使用如OC Runtime這樣的黑魔法,很多開源庫也是依賴其實(shí)現(xiàn)。因此OC和Swift混編應(yīng)該一個(gè)長期的趨勢,之前只是依賴于Xcode自動(dòng)引入bridge header等類似的機(jī)制,沒有仔細(xì)去理解,借著新做項(xiàng)目用到evernote oc庫的機(jī)會(huì)好好的總結(jié)一下。蘋果提供的Swift與Objective-C混編方案都是基于Xcode和LLVM編譯,采用Mix and Match機(jī)制。
從開發(fā)者實(shí)現(xiàn)角度根據(jù)不同的混編場景可以分為如下幾種情況:
普通代碼混編:項(xiàng)目內(nèi)普通代碼文件混編(.swift內(nèi)使用OC的.h和.m文件或者反過來,包括.a形式項(xiàng)目的開發(fā)),采用的bridge方案;
開發(fā)Framework混編:如果你的項(xiàng)目是輸出一個(gè)Framework,混編方式稍有不同,姑且成為umbrella方案;
引用外部Framework和宿主App混編:如果你的項(xiàng)目引用一個(gè)外部提供的Framework(無論這個(gè)Framework是單一語言開發(fā)還是本身就是混編的),混編方案也有不同。> 詳細(xì)的原理參見上文提到的官方文檔,本文主要關(guān)注三種方式的實(shí)現(xiàn)以及可能遇到的問題。
普通代碼文件混編方案:
Swift引用OC實(shí)現(xiàn)通過橋接頭文件,OC引用Swift實(shí)現(xiàn)直接importProductModuleName-Swift.h
這個(gè)文件即可。
OC引用Swift實(shí)現(xiàn)
ProductModuleName
在Build Settings
里面配置:
默認(rèn)用ProductName,可以支持自定義。(注明:Framework項(xiàng)目不支持自定義)
Swift引用OC實(shí)現(xiàn)
Swift引用OC實(shí)現(xiàn)稍微麻煩一點(diǎn),需要自己生成一個(gè)bridge header文件,和創(chuàng)建普通.h方式相同File > New > File > (iOS, watchOS, tvOS, or OS X) > Source > Header File
,名字隨意,然后配置到Build Settings - Swift Compiler - Code Generation
下的Objective-C Bridging Header
選項(xiàng)。
注意路徑從項(xiàng)目根目錄開始計(jì)算,可以使用..
來指定與根目錄平級目錄。bridge header內(nèi)import所有想要在swift中使用的OC類,就會(huì)作為一個(gè)module在swift中使用。例如:
#import "XYZCustomCell.h"
#import "XYZCustomView.h"
#import "XYZCustomViewController.h"
Swift中用如下代碼訪問:
let myOtherCell = XYZCustomCell()
myOtherCell.subtitle = "Another custom cell"
FYI. 語言類型為Swift的項(xiàng)目引入OC文件時(shí)Xcode會(huì)給個(gè)創(chuàng)建bridge header的提示,自己會(huì)配置了之后用處不大:
Framework項(xiàng)目中使用代碼混編方案:
Umbrella Header的相關(guān)知識蘋果沒有給出很明確的說明,只有以前介紹Umbrella Framework的時(shí)候介紹過,找了很久發(fā)現(xiàn)iOS - Umbrella Header在framework中的應(yīng)用這篇文章介紹的很好,詳細(xì)的內(nèi)容可以進(jìn)入了解。
Swift引用OC實(shí)現(xiàn)
現(xiàn)在我們只需要了解Framework里面Swift引用OC邏輯需要一個(gè)與ProductName同名的.h文件作為Umbrella Header,如果不存在則創(chuàng)建一個(gè)。不需要在Build Settings
配置因?yàn)檫@文件是map modules的時(shí)候自動(dòng)指定的,如果基于某種原因(比如這個(gè)同名文件已經(jīng)被用來寫其他邏輯)一定要自定義的話可以參考上面文章里介紹的方法。第二步到Build Settings - Packaging
中將Defines Module
選項(xiàng)設(shè)為YES
。然后將Swift中需要引用的OC邏輯引用進(jìn)來,訪問方式同普通代碼混編。
#import "XYZCustomCell.h"
#import "XYZCustomView.h"
#import "XYZCustomViewController.h"
OC引用Swift
實(shí)現(xiàn)OC引用Swift同樣需要將Defines Module
選項(xiàng)設(shè)為YES
,其余和普通代碼混編相比只是改了個(gè)引用文件的方式:#import
。
引用外部Framework時(shí)混編
方案:
重要前提
這里有一個(gè)重要的前提是這個(gè)外部Framework在編譯時(shí)必須開啟了Defines Module
,如果沒有開啟并且沒有Framework源碼的情況下還是繞路吧。
external framework混編
在這種情況下當(dāng)前App使用外部Framework是不關(guān)心其內(nèi)部到底是Swift實(shí)現(xiàn)、OC實(shí)現(xiàn)還是本身就是混編實(shí)現(xiàn)的。只需要Swift使用Framework邏輯時(shí)添加import FrameworkName
,OC使用時(shí)在任意.m
文件中添加@import FrameworkName;
語法即可。
混編后哪些邏輯可以被另一種語言引用到?
Swift中可以被OC引用的邏輯:
用
public
關(guān)鍵字;有bridging header的target中用
internal
關(guān)鍵字修飾;用
private
修飾的關(guān)鍵字通常是訪問不到的,除了@IBAction, @IBOutlet, 和 @objc標(biāo)記;
OC中由于開發(fā)習(xí)慣的原因基本上頭文件中的屬性、方法都可以被swift訪問到。
Evenote-Mac Framework混編時(shí)遇到的問題
-
Evenote-Mac
這個(gè)奇葩的Framework名字在生成umbrella header的時(shí)候報(bào)錯(cuò):
warning: EvernoteSDK-Mac is not a valid PRODUCT_NAME for use with framework targets enabling DEFINES_MODULE (name is not a valid C99 extended identifier)
warning: no umbrella header found for target 'EvernoteSDK-Mac', module map will not be generated
因?yàn)槊种杏?code>-字符,所以只能替換或者去掉;
- 改名時(shí)建議直接改target的名字,只改module的名字就會(huì)報(bào)錯(cuò):
Warning: PRODUCT_MODULE_NAME may not be overridden for framework target 'EvernoteSDKMac'