iOS混編相關(guān)知識(shí)點(diǎn)

前言

iOS混編也是老生常談的問(wèn)題,本篇文章我們將從實(shí)際開(kāi)發(fā)中不同使用場(chǎng)景來(lái)探究混編的相關(guān)知識(shí)點(diǎn)匿值,以便在實(shí)際項(xiàng)目中混編相關(guān)報(bào)錯(cuò)問(wèn)題的處理赠制。

  • 主工程內(nèi)混編
  • 主工程與依賴(lài)庫(kù)混編
  • 依賴(lài)庫(kù)內(nèi)部混編
主工程內(nèi)混編

主工程內(nèi)無(wú)外乎是Swift工程調(diào)用OC文件和OC工程調(diào)用Swift文件。
主工程為OC時(shí)挟憔,在第一次生成Swift文件時(shí)候钟些,系統(tǒng)會(huì)彈窗提示是否創(chuàng)建橋接文件ProjectName-Bridging-Header.h。
同樣的主工程為Swift時(shí)绊谭,在第一次生成OC文件時(shí)政恍,也會(huì)彈窗詢(xún)問(wèn)是否創(chuàng)建橋接文件。



橋接文件是為了將需要在Swift文件中使用到的OC文件的頭文件引入达传,編譯器則會(huì)將引入的所有oc文件進(jìn)行module化篙耗,即在swift的角度而言,引入的每個(gè)oc文件即為引入的每個(gè)module宪赶,這樣做的目的也主要是為了提高編譯速度宗弯,在編譯的過(guò)程中,編譯過(guò)的module會(huì)放入一個(gè)編譯好的modules數(shù)據(jù)結(jié)構(gòu)中逊朽,當(dāng)有重復(fù)的module引入時(shí)罕伯,dyld會(huì)將先前編譯好的module進(jìn)行l(wèi)ink產(chǎn)生最終的mach-o文件。

而如果是OC文件中使用Swift文件時(shí)叽讳,則需要在使用的OC文件中引入ProjectName-Swift.h文件追他,可在Targets -> Build Settings中查看。



該文件會(huì)在編譯的過(guò)程中產(chǎn)生岛蚤,其主要的目的是為了將Swift轉(zhuǎn)譯為OC邑狸,例如將一個(gè)類(lèi)暴露給OC的信息展示出來(lái),這樣在OC文件中就能直接使用了涤妒。
但由于Swift是靜態(tài)語(yǔ)言单雾,所以要想OC文件能訪(fǎng)問(wèn)到Swift的信息,例如一個(gè)類(lèi)的信息,則需要滿(mǎn)足以下若干條件:

  1. 類(lèi)繼承于NSObject基類(lèi)硅堆。
  2. 需要訪(fǎng)問(wèn)的屬性方法需要在繼承于基類(lèi)基礎(chǔ)之上再加上 @objc 修飾屿储。
  3. 如果想要使用方法交換,那么在以上基礎(chǔ)上還需要加上 dynamic 修飾(@objc 在前)渐逃。
依賴(lài)庫(kù)內(nèi)部混編
.modulemap文件

在正式探索這一塊知識(shí)之前够掠,我們先來(lái)了解下.modulemap文件,查看項(xiàng)目編譯后的AFNetworking.framework文件夾茄菊,標(biāo)準(zhǔn)的.framework依賴(lài)庫(kù)格式如下所示:



所有頭文件存放在Headers目錄下疯潭,點(diǎn)擊module.modulemap文件:

framework module AFNetworking {
  umbrella header "AFNetworking-umbrella.h"

  export *
  module * { export * }
}

在此module文件中所寫(xiě)代碼意思就是將AFNetworking.framework中的頭文件暴露給外界,通過(guò)AFNetworking-umbrella.h這個(gè)文件面殖,查看此文件如下:

#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif

#import "AFHTTPSessionManager.h"
#import "AFURLSessionManager.h"
#import "AFCompatibilityMacros.h"
#import "AFNetworkReachabilityManager.h"
#import "AFSecurityPolicy.h"
#import "AFURLRequestSerialization.h"
#import "AFURLResponseSerialization.h"

FOUNDATION_EXPORT double AFNetworkingVersionNumber;
FOUNDATION_EXPORT const unsigned char AFNetworkingVersionString[];

類(lèi)似于上方的橋接文件竖哩,AFNetworking-umbrella.h也是給外界暴露依賴(lài)庫(kù)的頭文件。
framework module AFNetworking:標(biāo)準(zhǔn)寫(xiě)法脊僚,將AFNetworking設(shè)為一個(gè)名為AFNetworking的模塊相叁。
umbrella header "AFNetworking-umbrella.h":設(shè)置此模塊的傘文件,傘文件中引入了庫(kù)中的頭文件辽幌。
export *:將AFNetworking庫(kù)中本來(lái)依賴(lài)的其余東西帶出去钝荡。
module * { export * }:如果存在子模塊時(shí)候,繼續(xù)將子模塊及子模塊依賴(lài)的所有東西一起帶出去舶衬。

自定義.framework

例如上方工程中埠通,需要使用LcrTiger類(lèi),常規(guī)引入頭文件方法為#import "LcrTiger.h"逛犹、#import "Head/LcrTiger.h"端辱、絕對(duì)路徑#import "/Users/lichuanrong/Desktop/LcrApp/LcrApp/Head/LcrTiger.h"三種方式。
但是我們利用.modulemap文件將LcrTiger包裝成一個(gè)模塊虽画,然后利用@import Tiger; 來(lái)引入Tiger類(lèi)舞蔽。
Xcode配置文件LcrApp-Debug.xcconfig中給項(xiàng)目增加module文件路徑如下:

OTHER_CFLAGS = "-fmodule-map-file=${SRCROOT}/LcrApp/Head/TigerModule.modulemap"

TigerModule.modulemap代碼如下:

module Tiger {
    header "LcrTiger.h"
    export *
}

如果將export *注釋掉,則LcrTuger.m中的外部符號(hào)NSLog就會(huì)報(bào)錯(cuò):



所以export *的作用也就不用再解釋了吧码撰。

接下來(lái)我們手動(dòng)將Head文件夾封裝成.framework靜態(tài)庫(kù)渗柿,仿照上方AFNetworking.framework標(biāo)準(zhǔn)樣式。


  1. 將Head文件夾更改為Head.framework脖岛。
  2. 新建Headers文件夾存放頭文件LcrTiger.h朵栖。
  3. 修改TigerModule文件,添加framework 前綴柴梆。
framework module Tiger {
    header "LcrTiger.h"
    export *
}
  1. 修改config文件中TigerModule文件路徑陨溅。

同樣利用@import Tiger;即可引入此模塊。

子模塊及傘文件
新建LcrDog類(lèi)绍在,將LcrDog.h拖進(jìn)Headers文件夾內(nèi)门扇,新建Animal-umbrella.h傘文件雹有。


在Animal-umbrella.h文件中分別引入LcrTiger.h、LcrDog.h兩個(gè)頭文件臼寄,更改TigerModule如下:

framework module Animal {
    umbrella header "Animal-umbrella.h"
    export *
}

通過(guò)添加 umbrella 修飾符霸奕,將Animal-umbrella.h文件作為模塊Animal的傘文件,其中以引入了外界需要使用的類(lèi)的頭文件吉拳,所以外界使用@import Animal;引入整個(gè)模塊即可铅祸。
這樣一個(gè)簡(jiǎn)單的.framework就制作好了。
優(yōu)化
如果一個(gè)庫(kù)文件內(nèi)也分幾個(gè)小模塊合武,那么就需要?jiǎng)?chuàng)建子Module,例如將上方的LcrTiger涡扼、LcrDog分別作為小模塊稼跳,如下所示:

framework module Animal {
    umbrella header "Animal-umbrella.h"
    export *
    
    module LcrTiger {
        header "LcrTiger.h"
        export *
    }
    
    module LcrDog {
        header "LcrDog.h"
        export *
    }
}

此時(shí)@import Animal;引入依然是沒(méi)問(wèn)題的,注釋掉Animal-umbrella.h文件中的引入都是沒(méi)啥問(wèn)題的吃沪。也可分別引入子模塊:

@import Animal.LcrTiger;
@import Animal.LcrDog;
  1. 當(dāng)需要顯式地引入相應(yīng)子模塊時(shí)汤善,就需要在相應(yīng)子模塊前添加explicit 修飾符,注釋掉Animal-umbrella.h文件中的引入票彪,如果還是利用@import Animal;的話(huà)红淡,那么就會(huì)報(bào)錯(cuò),解決方法就是@import Animal.LcrTiger;顯式地引入降铸。
  2. 如果子Module中不制定頭文件在旱,那么就不能注釋掉Animal-umbrella.h文件中的引入,因?yàn)檫@樣寫(xiě)的話(huà)推掸,系統(tǒng)就會(huì)默認(rèn)將傘文件中的頭文件看作是一個(gè)個(gè)子Module桶蝎。
framework module Animal {
    umbrella header "Animal-umbrella.h"
    export *
    
    module * {
        export *
    }
}
//Animal-umbrella.h
#import <Head/LcrTiger.h>
#import <Head/LcrDog.h>

注:(使用子模塊的好處就是如果庫(kù)中頭文件太多,可以將部分用到的文件放入子模塊谅畅,顯式引入子模塊即可登渣,提高代碼效率,例如庫(kù)中有100個(gè)頭文件毡泻,80個(gè)放入傘文件中胜茧,而我需要使用另外20個(gè),那么就可以將另外20個(gè)放入子模塊中仇味,那80個(gè)我不用就不引入呻顽,就不參與編譯,大大節(jié)省時(shí)間)

子模塊間關(guān)系
子模塊間如果有引用關(guān)系丹墨,例如LcrTiger中使用到LcrDog芬位,可利用@import LcrDog;先將LcrDog子模塊引入,然后export LcrDog發(fā)射出去带到,外界LcrDog也可用昧碉。

framework module Animal {
    umbrella header "Animal-umbrella.h"
    export *
}

module LcrDog {
    header "Headers/LcrDog.h"
    export *
}

module LcrTiger {
    header "Headers/LcrTiger.h"
    export LcrDog
    export *
}

拓展--requires

framework module Animal {
    umbrella header "Animal-umbrella.h"
    export *
}

//點(diǎn)語(yǔ)法 默認(rèn)是Animal的子module
//requires objc:使用Animal.Swift 模塊的源文件必須是OC文件英染。
module Animal.Swift {
    header "Headers/LcrTiger.h"
    requires objc
}

創(chuàng)建SwiftFrameW.framework項(xiàng)目,項(xiàng)目初始語(yǔ)言為Swift被饿,新建Teacher.swift文件四康,以及OC類(lèi)Lcr:



注意:在上文主工程內(nèi)混編中,系統(tǒng)會(huì)彈窗提示是否需要橋接文件狭握,但在這個(gè)項(xiàng)目目前的過(guò)程中沒(méi)有闪金,也就是說(shuō)沒(méi)有橋接文件,swift文件如何使用oc信息论颅。
那么如上所示的想要在Teacher.swift文件中使用Lcr類(lèi)哎垦,則需要將引入到SwiftFrameW.h頭文件中,這個(gè)文件時(shí)framwork創(chuàng)建時(shí)就自帶的恃疯,作用類(lèi)似上文中的傘文件漏设,#import "Lcr.h" 發(fā)現(xiàn)報(bào)錯(cuò)如下:

Include of non-modular header inside framework module 'SwiftFrameW': '/Users/lichuanrong/Desktop/SwiftFrameW/SwiftFrameW/Objc/Lcr.h'

此時(shí)需要將Lcr.h頭文件移到public中,以便對(duì)外暴露今妄,在public中的頭文件最終編譯后存放在Headers文件夾中郑口,外界訪(fǎng)問(wèn)庫(kù)文件時(shí)就是通過(guò)訪(fǎng)問(wèn)Headers中的頭文件的。



再次編譯盾鳞,報(bào)錯(cuò)消失了犬性。

那么如果Lcr.m中使用Teacher,方法與常規(guī)主工程中方法一樣腾仅,需要引入SwiftFrameW-Swift.h文件乒裆,就是寫(xiě)法不一樣:

#import <SwiftFrameW/SwiftFrameW-Swift.h>

那如果是利用modulemap的方式呢?

  1. 新建module.modulemap文件推励。
  2. 新建xcconfig文件配置參數(shù)缸兔。(PROJECT里面設(shè)置好debug模式)
  3. 注釋SwiftFrameW.h里對(duì)Lcr.h的引用。



    發(fā)現(xiàn)編譯錯(cuò)誤吹艇,因?yàn)闀簳r(shí)還沒(méi)引用惰蜜,即找不到相應(yīng)的頭文件。
    要不就將SwiftFrameW.h里的注釋打開(kāi)受神,要不就將modulemap文件修改如下:

framework module SwiftFrameW {
    umbrella header "SwiftFrameW.h"
    export *
    
    module Lcr {
        header "Lcr.h"
        export *
    }
    
    module * {
        export *
    }
}

這樣編譯就不報(bào)錯(cuò)了抛猖。

Public Private Project
上方是將Lcr.h頭文件放在Public中,編譯產(chǎn)物中鼻听,Lcr.h存放在Headers文件夾中财著,如果將它拖進(jìn)Private中,編譯依然沒(méi)問(wèn)題撑碴,編譯產(chǎn)物中Lcr.h就被放入到PrivateHeaders文件夾中撑教。


在Teacher中依然能夠訪(fǎng)問(wèn)到Lcr類(lèi)信息,但是如果將Lcr.h設(shè)為單獨(dú)的一個(gè)Private的模塊醉拓,如下:

利用import SwiftFrameW_Private 依然能通過(guò)編譯伟姐,說(shuō)明不管Lcr.h放在Public還是Private中都能被外界訪(fǎng)問(wèn)到收苏。

主工程與依賴(lài)混編

利用上方創(chuàng)建的主工程和framework靜態(tài)庫(kù)合并成一個(gè)xcworkspace,且主工程引入SwiftFrameW.framework愤兵,探索主工程與依賴(lài)庫(kù)之間的混編鹿霸。



如上圖所示,主工程依賴(lài)SwiftFrameW庫(kù)秆乳,之間的混編如下:

  1. 主工程O(píng)C訪(fǎng)問(wèn)依賴(lài)庫(kù)OC文件:#import <SwiftFrameW/Lcr.h> 或者 @import SwiftFrameW;
  2. 主工程O(píng)C訪(fǎng)問(wèn)依賴(lài)庫(kù)Swift文件:#import <SwiftFrameW/SwiftFrameW-Swift.h>懦鼠,注意Swift 中需要用public或open修飾,因?yàn)槭强鐃arget的屹堰。
  3. 主工程O(píng)C訪(fǎng)問(wèn)主工程Swift文件:#import "工程名-Swift.h"
  4. 主工程Swift訪(fǎng)問(wèn)依賴(lài)庫(kù)OC和Swift文件:@import SwiftFrameW
  5. 主工程Swift訪(fǎng)問(wèn)主工程O(píng)C文件: 工程名-Bridging-Header.h內(nèi)引入頭文件即可肛冶。

更多詳情訪(fǎng)問(wèn):
Swift與OC混編
OC項(xiàng)目倒入Swift三方庫(kù)不兼容問(wèn)題
Swift調(diào)用OC

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扯键,隨后出現(xiàn)的幾起案子睦袖,更是在濱河造成了極大的恐慌,老刑警劉巖忧陪,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異近范,居然都是意外死亡嘶摊,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)评矩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)叶堆,“玉大人,你說(shuō)我怎么就攤上這事斥杜∈牛” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵蔗喂,是天一觀(guān)的道長(zhǎng)忘渔。 經(jīng)常有香客問(wèn)我,道長(zhǎng)缰儿,這世上最難降的妖魔是什么畦粮? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乖阵,結(jié)果婚禮上宣赔,老公的妹妹穿的比我還像新娘。我一直安慰自己瞪浸,他們只是感情好儒将,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著对蒲,像睡著了一般钩蚊。 火紅的嫁衣襯著肌膚如雪贡翘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天两疚,我揣著相機(jī)與錄音床估,去河邊找鬼。 笑死诱渤,一個(gè)胖子當(dāng)著我的面吹牛丐巫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播勺美,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼递胧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了赡茸?” 一聲冷哼從身側(cè)響起缎脾,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎占卧,沒(méi)想到半個(gè)月后遗菠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡华蜒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年辙纬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叭喜。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡贺拣,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捂蕴,到底是詐尸還是另有隱情譬涡,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布啥辨,位于F島的核電站涡匀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏溉知。R本人自食惡果不足惜渊跋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望着倾。 院中可真熱鬧拾酝,春花似錦、人聲如沸卡者。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)崇决。三九已至材诽,卻和暖如春底挫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脸侥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工建邓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人睁枕。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓官边,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親外遇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子注簿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容