Let us write swift(解決use_framework!時第三方庫是static library)

特別感謝團隊小伙伴(李龍龍、崇慶旭)

越小的團隊求變化耘柱,越大的團隊求穩(wěn)定猎物。作為一個小團隊我們又開始折騰了。不用再贅述為什么要轉(zhuǎn)swift淮椰。唯一能阻止我們的問題是:

target has transitive dependencies that include static binaries

這個問題來自于使用cocoapods組件化開發(fā)五慈。使用swift時必須use_frameworks!,然后我們不得不依賴一些第三方庫主穗,這些第三方庫是static library(哪些庫就不點名了泻拦,大家心里都應(yīng)該有數(shù))。pod install時就能看到上面的內(nèi)容了黔牵。

本來想先分析下問題的原因的,但是怕各位看官沒有心思看爷肝。所以先講解決猾浦,再分析吧陆错。分別有上中下策任君選擇。

上策

等cocopods官方的1.4.x正式版金赦,會提供static_framework

CocoaPods version 1.3.1 and earlier do not support static framework dependencies.CocoaPods 1.4.0 adds thestatic_framework option in #6811 that enables you to specify building a pod as a static_framework, which unlike dynamic frameworks, can have static framework dependencies.

鏈接參考1
鏈接參考2
截止這篇文章寫的時候音瓷,還不能用。

上策的好處是官方的夹抗,改動最小绳慎。

中策

如果你等不起官方,可以使用這個方案漠烧。這個方案寫的非常非常之詳盡杏愤,你一定能額外收獲不少知識。

這個問題的思路簡單講就是用一個動態(tài)庫吸附一個靜態(tài)庫已脓,然后因為動態(tài)庫的隔離性巧妙地解決了這個問題珊楼。

親測,可以度液。但是在做的時候遇到了小問題厕宗,分享一下。

第一個問題是出現(xiàn)了warning:
Missing sub module
可能是我們理解能力有問題堕担,一開始沒有解決已慢。特此分享一下,也許大家一看就明白怎么做了霹购。

比如包裝了一個XXX.framework佑惠。里面的Headers,有一個XXX.h厕鹃。需要在XXX.h中導(dǎo)入其他頭文件兢仰。

XXX.framework
├── Headers
│   ├── XXX.h
│   ├── WXApi.h
│   ├── WXApiObject.h
│   └── WechatAuthSDK.h
├── Info.plist
├── XXX
├── Modules
│   └── module.modulemap
└── _CodeSignature
    └── CodeResources

XXX.h需要這樣

#import <XXX/WXApi.h>
#import <XXX/WXApiObject.h>
#import <XXX/WechatAuthSDK.h>

第二個問題是:
直接發(fā)cocopods庫發(fā)出來,千萬不要直接放入到一個組件項目中直接用剂碴。這樣會找不到頭文件把将。

中策的好處是還可以用組件化開發(fā)的方式依賴包裝過的第三方庫。改動也很小忆矛,但是需要做很多體力活包庫發(fā)庫察蹲。

下策

我們發(fā)現(xiàn)在主項目的podfile依賴第三方庫(static library),用use_framework!不會有問題催训,原因后面分析洽议。所以我們想把這些第三方庫都放在主項目(App)中。要解決的是其他組件如何使用這些第三方庫漫拭。

其實思路很簡單亚兄,就是組件化開發(fā)中的業(yè)務(wù)組件相互隔離通過中間件通訊。我們用的是協(xié)議的方式采驻。用協(xié)議的好處是比較優(yōu)雅审胚,協(xié)議方法等照抄第三庫就可以了匈勋。

下策.png

首先。我們創(chuàng)建了一個ModuleManager類取名叫做NBUtilProtocolManager,這個manager主要負責(zé)收集協(xié)議和下發(fā)協(xié)議膳叨。這個manager就提供兩個方法在NBUtilProtocolManager.h中

#import <Foundation/Foundation.h>

@interface NBUtilProtocolManager : NSObject

+ (void)registServiceProvide:(_Nonnull id)provide forProtocol:(nonnull Protocol *)protocol;

+ (_Nonnull id)serviceProvideForProtocol:(nonnull Protocol *)protocol;

@end

在NBUtilProtocolManager.m中

#import "NBUtilProtocolManager.h"

@interface NBUtilProtocolManager ()

@property (nonatomic, strong) NSMutableDictionary *serviceProvideSource;

@end

@implementation NBUtilProtocolManager

+ (NBUtilProtocolManager *)sharedInstance
{
    static NBUtilProtocolManager * instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _serviceProvideSource = [[NSMutableDictionary alloc] init];
    }
    return self;
}

+ (void)registServiceProvide:(id)provide forProtocol:(Protocol *)protocol
{
    if (provide == nil || protocol == nil)
        return;
    [[self sharedInstance].serviceProvideSource setObject:provide forKey:NSStringFromProtocol(protocol)];
}

+ (id)serviceProvideForProtocol:(Protocol *)protocol
{
    return [[self sharedInstance].serviceProvideSource objectForKey:NSStringFromProtocol(protocol)];
}

@end

manager創(chuàng)建完成后發(fā)布洽洁,作為基礎(chǔ)組件給個個業(yè)務(wù)組件使用。

有個這個manager以后菲嘴,我們就開始定義一個協(xié)議NBShareSDKProtocol.h這個協(xié)議里面有一個分享方法饿自,協(xié)議里面的方法無論是返回值還是參數(shù)都是我們自己組件能識別的 和shareSDK沒有關(guān)系.代碼如下

- (nonnull RACSignal *)shareContentWithInfo:(nonnull NSDictionary *)info;

協(xié)議定義完成后發(fā)布,作為基礎(chǔ)組件給個個業(yè)務(wù)組件使用龄坪。

這個時候我們就開始定義一個NBShareSDKObject的類來實現(xiàn)協(xié)議中的方法昭雌。代碼如下

@interface NBShareSDKObject () <NBShareSDKProtocol>

@end

@implementation NBShareSDKObject

@synthesize defaultShareImageUrl;

+ (void)load
{
    [NBUtilProtocolManager registServiceProvide:[[self alloc] init] forProtocol:@protocol(NBShareSDKProtocol)];
}

- (nonnull RACSignal *)shareContentWithInfo:(nonnull NSDictionary *)info {
    return ....;
}

注意在這個類中的load方法中使用NBUtilProtocolManager將這個協(xié)議的實現(xiàn)者給加入進去。load 方法會在加載類的時候就被調(diào)用悉默,也就是 ios 應(yīng)用啟動的時候城豁,就會加載所有的類,就會調(diào)用每個類的 + load 方法抄课。在這個時機注入?yún)f(xié)議的實現(xiàn)者是比較合適的時機唱星。這樣的話在項目啟動的時候manager里面就已經(jīng)包含了這個協(xié)議的實現(xiàn)者。協(xié)議的實現(xiàn)都在主項目里面完成.這樣所有的準備工作都已經(jīng)完成跟磨。接下來就是如何使用的問題间聊。

在業(yè)務(wù)組件的podfile中我們要依賴NBUtilProtocolManager和NBShareSDKProtocol這個自己發(fā)布的庫。然后在代碼中導(dǎo)入頭文件后按照如下代碼:

    id <NBShareSDKProtocol> obj = [NBUtilProtocolManager serviceProvideForProtocol:@protocol(NBShareSDKProtocol)];

    [obj shareContentWithInfo:@{@"url" : @"www.baidu.com"}];

下策是保底方案抵拘。修改多哎榴,體力活多。

這個下策也是這個issue的下策解決方案

Pod lint fails when containing dynamic-frameworks without simulator architectures

這個問題的上策是等官方PR最終發(fā)出來僵蛛。

中策是用skip-import-validation尚蝌。

下策的方案本質(zhì)是隔離。不經(jīng)感嘆組件化開發(fā)的本質(zhì)也是隔離充尉。代碼隔離飘言,邏輯隔離,業(yè)務(wù)隔離驼侠,人員隔離姿鸿,開發(fā)隔離,發(fā)布隔離倒源。對內(nèi)自洽苛预,對外隔離。

分析

target has transitive dependencies that include static binaries

這個錯誤笋熬,owner早在2014.11.24在這個issue下進行了說明热某。

Static libraries and frameworks are only linked to the user target by classic CocoaPods. That approach will not work if we are building frameworks and have a vendored static library as a
because the linker expects all symbols to be resolved in this case.

Using -undefined dynamic_lookup or linking to both the dynamic framework and the user target could be solutions to pursue for the future, but as a first step, we should reject these cases.

To reject vendored_libraries we can simply use the file extension, for vendored_frameworks we have to check if the included binary is statically or dynamically linked.

owner鮮明的指出了"我們應(yīng)當(dāng)拒絕這種情況"。最后一段講了:對于vendored_libraries(.a),我們可以直接判斷文件后綴名昔馋,對于framework芜繁,需要檢查是statically or dynamically。我們?nèi)绻枰约簷z查的話可以使用file xxx.framework/xxx
第一段的問題用圖來講解

transitive_dependencies.png

還有個問題是绒极, umbrella header中有傳遞頭文件。

I guess, that happens because public headers are imported by the umbrella header and will have implicit clang module exports. That also includes transitive imported headers. If the imported header is not within the same target, it can't probably be exported as nested module, if it isn't already part of a clang module itself.

為什么在podfile依賴中可以蔬捷,在pod spec中不可以呢垄提?因為pod spec中寫dependency是transitive dependencies。


podfile_dependency.png

圖不一定很準確周拐,大概是這個意思铡俐。

那為什么動態(tài)庫依賴動態(tài)庫就可以呢?具體理論可以參考這篇博文(也就是中策)中說的動態(tài)庫的隔離性妥粟,和動態(tài)庫在鏈接時不copy而是在啟動時交給dyld审丘。

為什么中策可以呢?來個圖勾给。


solution.png

最后

蘋果官方在XCode9支持了static swift library滩报。這說明swift版本已趨于穩(wěn)定了。附上鏈接在Building and Linking章節(jié)播急。也許也是因為這一點cocoapods官方也開始想要提供static_framework這個配置吧脓钾。

沒有什么點可以阻止我們了,讓我們開始寫swift吧桩警。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末可训,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子捶枢,更是在濱河造成了極大的恐慌握截,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烂叔,死亡現(xiàn)場離奇詭異谨胞,居然都是意外死亡,警方通過查閱死者的電腦和手機长已,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門畜眨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人术瓮,你說我怎么就攤上這事康聂。” “怎么了胞四?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵恬汁,是天一觀的道長。 經(jīng)常有香客問我辜伟,道長氓侧,這世上最難降的妖魔是什么脊另? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮约巷,結(jié)果婚禮上偎痛,老公的妹妹穿的比我還像新娘。我一直安慰自己独郎,他們只是感情好踩麦,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著氓癌,像睡著了一般谓谦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上贪婉,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天反粥,我揣著相機與錄音,去河邊找鬼疲迂。 笑死才顿,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尤蒿。 我是一名探鬼主播娜膘,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼优质!你這毒婦竟也來了竣贪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤巩螃,失蹤者是張志新(化名)和其女友劉穎演怎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體避乏,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡爷耀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了拍皮。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歹叮。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铆帽,靈堂內(nèi)的尸體忽然破棺而出咆耿,到底是詐尸還是另有隱情,我是刑警寧澤爹橱,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布萨螺,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏慰技。R本人自食惡果不足惜椭盏,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吻商。 院中可真熱鬧掏颊,春花似錦、人聲如沸艾帐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掩蛤。三九已至,卻和暖如春陈肛,著一層夾襖步出監(jiān)牢的瞬間揍鸟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工句旱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留阳藻,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓谈撒,卻偏偏與公主長得像腥泥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子啃匿,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344