成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

抖音平臺(tái)多產(chǎn)物代碼隔離技術(shù)的實(shí)踐與探索

原創(chuàng) 精選
開(kāi)發(fā)
隨著抖音業(yè)務(wù)的發(fā)展,為保障整體工程演進(jìn)和迭代計(jì)劃的高效運(yùn)行,體系化建設(shè)已加速提上日程,Codebase(可通稱(chēng)為產(chǎn)物)融合是其中項(xiàng)目之一。該項(xiàng)目主要為開(kāi)發(fā)同學(xué)提供底層復(fù)用能力、增強(qiáng)研發(fā)團(tuán)隊(duì)效能,致力于幫助開(kāi)發(fā)同學(xué)輕松高效地研發(fā)、管理代碼。

作者|郭玉

前言介紹

在軟件架構(gòu)領(lǐng)域,框架的功能類(lèi)似于基礎(chǔ)設(shè)施服務(wù),是為實(shí)現(xiàn)某個(gè)業(yè)界標(biāo)準(zhǔn)而形成的組件規(guī)范。簡(jiǎn)單理解,框架就是制定一套規(guī)范或者規(guī)則,開(kāi)發(fā)同學(xué)在該規(guī)范或者規(guī)則下工作。本文通過(guò)剖析框架實(shí)體 ServiceKit/Adapter ,來(lái)窺探其底層結(jié)構(gòu)和架構(gòu)設(shè)計(jì)。

背景描述

隨著抖音業(yè)務(wù)的發(fā)展,為保障整體工程演進(jìn)和迭代計(jì)劃的高效運(yùn)行,體系化建設(shè)已加速提上日程,Codebase(可通稱(chēng)為產(chǎn)物)融合是其中項(xiàng)目之一。該項(xiàng)目主要為開(kāi)發(fā)同學(xué)提供底層復(fù)用能力、增強(qiáng)研發(fā)團(tuán)隊(duì)效能,致力于幫助開(kāi)發(fā)同學(xué)輕松高效地研發(fā)、管理代碼。

圖片

Codebase 融合過(guò)程中,技術(shù)團(tuán)隊(duì)在各個(gè)業(yè)務(wù)線方向進(jìn)行著差異化探索;演進(jìn)路程上,業(yè)務(wù)線間耦合越來(lái)越強(qiáng),開(kāi)發(fā)同學(xué)迫切需要一套解決方案來(lái)做差異化代碼隔離。如下圖抖音與抖音極速版模塊差異所示。

圖片

回顧痛點(diǎn),在過(guò)往的開(kāi)發(fā)中,開(kāi)發(fā)者們一般使用宏隔離( isLite or isPad )來(lái)區(qū)分不同產(chǎn)物之間的差異,但這種方式嚴(yán)重破壞了整個(gè)抖音工程的架構(gòu)體系,以下從幾個(gè)維度分析。

研發(fā)效率:需要支持不同宏變量進(jìn)行 lint ,有重復(fù) lint ,單個(gè)組件很難區(qū)分項(xiàng)目控制二進(jìn)制發(fā)版頻率,二進(jìn)制需要頻繁更新,宏會(huì)導(dǎo)致很多混編二進(jìn)制,影響編譯效率,如果以單個(gè)文件作為編譯緩存單元,宏隔離也會(huì)降低編譯緩存命中率。

可擴(kuò)展性:擴(kuò)展性差,缺乏動(dòng)態(tài)能力和插件能力,添加新功能和修改原有功能會(huì)導(dǎo)致類(lèi)實(shí)現(xiàn)的代碼急劇膨脹。

圈復(fù)雜度:宏隔離的代碼分散,修改和重構(gòu)成本高。

組件粒度:無(wú)法支持項(xiàng)目間差異業(yè)務(wù)獨(dú)立成組件,背離高內(nèi)聚、低耦合原則。

我們的目標(biāo)愿景是要做一套符合抖音工程架構(gòu)體系,具備高效、通用、便捷能力的框架規(guī)范,讓開(kāi)發(fā)同學(xué)在標(biāo)準(zhǔn)規(guī)則下進(jìn)行編碼工作。

架構(gòu)設(shè)計(jì)

啟蒙圖紙

啟蒙設(shè)計(jì)是著手做事之前的抽象意識(shí),如下圖,在多個(gè)產(chǎn)物的研發(fā)環(huán)境下,將共同代碼能高效的復(fù)用,差異性代碼優(yōu)雅的隔離開(kāi)。

圖片

為了幫助新同學(xué)快速入手架構(gòu)框架,筆者在做此框架 Swift 建設(shè)的過(guò)程中,基于近段時(shí)間經(jīng)歷的幾個(gè)項(xiàng)目經(jīng)驗(yàn),總結(jié)出了一套系統(tǒng)性的腦圖,下面和大家分享下框架系統(tǒng)化的全景。

框架全景思維

內(nèi)容較多,但是全景思維還是想要在這里提一下,說(shuō)不定在哪個(gè)階段上給你靈感;建議從樹(shù)的根節(jié)點(diǎn)出發(fā),選擇性的去了解它;如想大致了解,只用進(jìn)入到 3 層左右,如想深入了解,請(qǐng)走到葉子節(jié)點(diǎn)(為了不影響閱讀體驗(yàn),更加細(xì)節(jié)的節(jié)點(diǎn)已經(jīng)被裁剪掉)。

圖片

基于上述框架系統(tǒng)化的思路鋪開(kāi),整個(gè)篇章會(huì)先介紹一些設(shè)計(jì)思想,再進(jìn)行性能等相關(guān)的技術(shù)細(xì)節(jié)。由于篇幅有限,我們將精簡(jiǎn)出我們認(rèn)為比較重要的技術(shù)點(diǎn)進(jìn)行重點(diǎn)講解。

設(shè)計(jì)思想

適配器模式

在設(shè)計(jì)模式中,適配器模式(adapter pattern)有時(shí)候也稱(chēng)包裝樣式或者包裝。將一個(gè)類(lèi)的接口轉(zhuǎn)接成用戶(hù)所期待的。一個(gè)適配使得因接口不兼容而不能在一起工作的類(lèi)能在一起工作,做法是將類(lèi)自己的接口包裹在一個(gè)已存在的類(lèi)中。

—— 維基 百科 適配器模式

圖片

開(kāi)發(fā)同學(xué)不用關(guān)心各個(gè)模塊的復(fù)雜度、業(yè)務(wù)的邏輯性、是選擇類(lèi)對(duì)象還是實(shí)例對(duì)象、如何初始化各自單元等,僅需要基于包裝好的適配器來(lái)做各自的任務(wù)調(diào)度,類(lèi)似于萬(wàn)能充電器( 90 后同學(xué)時(shí)代的產(chǎn)物 :> ),無(wú)需關(guān)注電池是華為的,還是 OPPO 的,即插即用。

注冊(cè)與發(fā)現(xiàn)

服務(wù)注冊(cè) - 服務(wù)發(fā)現(xiàn)思想

  • 服務(wù)演進(jìn)

下面三個(gè)圖簡(jiǎn)單描述了 web 服務(wù)時(shí)代從傳統(tǒng)服務(wù)到微服務(wù)時(shí)代的歷程(傳統(tǒng)服務(wù) -> 并發(fā)服務(wù) -> 分布式微服務(wù)),大家感興趣可以了解下,這里不過(guò)多介紹。

圖片

圖片

圖片

  • 微服務(wù)

微服務(wù)是一種以業(yè)務(wù)功能為主的服務(wù)設(shè)計(jì)概念,每一個(gè)服務(wù)都具有自主運(yùn)行的業(yè)務(wù)功能,對(duì)外開(kāi)放不受語(yǔ)言限制的 API ,應(yīng)用程序則是由一個(gè)或多個(gè)微服務(wù)組成。

—— 維基百科,微服務(wù)

簡(jiǎn)單了解微服務(wù)后,以服務(wù)角度來(lái)看,多個(gè) Target 產(chǎn)物根據(jù)各業(yè)務(wù)模塊可劃分為多個(gè) Adapter 服務(wù),搭配綁定多個(gè)適配器協(xié)議,這樣能達(dá)成一對(duì)多效果。

我們深入性的介紹下內(nèi)部設(shè)計(jì)思路。在使用階段,一個(gè)主類(lèi)可以向多個(gè)適配器類(lèi)發(fā)送消息;在注冊(cè)過(guò)程,一個(gè)適配器類(lèi)可以綁定到多個(gè)適配器協(xié)議,并且滿(mǎn)足兩種場(chǎng)景:一是多產(chǎn)物必須實(shí)現(xiàn)的接口,可以放在一個(gè)公共的協(xié)議上,二是單個(gè)產(chǎn)物必須實(shí)現(xiàn)的接口放在獨(dú)立的協(xié)議上,公共協(xié)議 + 獨(dú)立協(xié)議可以進(jìn)行組合,由同一個(gè)有上下文關(guān)聯(lián)的適配器類(lèi)來(lái)實(shí)現(xiàn)。

圖片

提到微服務(wù),我們不得不了解下兩個(gè)概念,服務(wù)注冊(cè)與發(fā)現(xiàn)。

服務(wù)注冊(cè)

  • 服務(wù)注冊(cè):是將提供某個(gè)服務(wù)的模塊信息注冊(cè)到一個(gè)公共的組件上去。(如下示例代碼更加容易理解)
//服務(wù)注冊(cè)
ServiceKit.register(AModuleServer);

服務(wù)發(fā)現(xiàn)

  • 服務(wù)發(fā)現(xiàn):是指使用一個(gè)注冊(cè)中心來(lái)記錄分布式系統(tǒng)中的全部服務(wù)的信息,以便其他服務(wù)能夠快速的找到這些已注冊(cè)的服務(wù);不管是服務(wù)新增和服務(wù)刪減都能實(shí)現(xiàn)自動(dòng)發(fā)現(xiàn)。(如下示例代碼更加容易理解)
//服務(wù)發(fā)現(xiàn)
ServiceKit.get(AModuleServer);

進(jìn)階圖紙

圖片

  • 藍(lán)色框:抖音 Target
  • 黑色框:抖音極速版 Target
  • aXXXDOUYINAdapter:是 XXXDOUYINAdapterImpl 的服務(wù)實(shí)例。
  • XXXDOUYINAdapterImpl:是訂閱者,發(fā)布者是持有 XXXDOUYINAdapterImpl 實(shí)例 XXXDOUYINAdapter 的主類(lèi)。
  • <>XXXDOUYINAdapter:面向協(xié)議編程,抽象 Protocol 接口,抽離各自差異性、公共性代碼的接口。

上圖再一步概括了整個(gè)項(xiàng)目背景(抖音、抖音極速版的兩套代碼,有重復(fù)也有差異,如何將重復(fù)的代碼繼續(xù)共用,并且將差異性的代碼隔離到各自的Target產(chǎn)物中,不再耦合)、我們要做的過(guò)程(通過(guò)適配器模式來(lái)做任務(wù)調(diào)度,面向協(xié)議編程,抽離共用、差異性的代碼為接口形式,在各自Target中,實(shí)現(xiàn)各自的協(xié)議Impl),以及達(dá)成的結(jié)果(通過(guò)便利性腳手架、輔助工具能讓使用者低成本學(xué)習(xí)和理解,容易上手操作)。

關(guān)系圖紙

圖片

工程視角

從抖音現(xiàn)有工程架構(gòu)視角,了解設(shè)計(jì)。

圖片

流程實(shí)戰(zhàn)

接下來(lái)我們進(jìn)行下流程性實(shí)戰(zhàn)演練。

代碼實(shí)戰(zhàn)中,訂閱類(lèi)在 App 內(nèi)存創(chuàng)建一個(gè)實(shí)例,訂閱者的生命周期由所有關(guān)聯(lián)的發(fā)布者決定,比如多個(gè)控制器匯總埋點(diǎn)邏輯到一個(gè)加工者, 或比如一個(gè)父控制器對(duì)應(yīng)多個(gè)子控制器。

圖片

技術(shù)細(xì)節(jié)

上述了解設(shè)計(jì)性圖紙之后,我們深入淺出的剖析內(nèi)部技術(shù)細(xì)節(jié)。

編譯插拔

常規(guī)思路下,注冊(cè)會(huì)放到 App 啟動(dòng)階段,但這樣做容易拖緩 App 的啟動(dòng)速度。要想做到在最早的時(shí)機(jī)注冊(cè)但又不影響啟動(dòng)速度,需要基于編譯器特性:__attribute__((section("name"))) 實(shí)現(xiàn),通過(guò) attribute 指令,編譯時(shí)期寫(xiě)在 .data 段,然后在運(yùn)行時(shí)期讀出來(lái)。下圖介紹編譯注解的簡(jiǎn)單流程。

圖片

代碼示例

__attribute((used, section(_DY_SEGMENT "," _DY_MSG_ASSOCIATE_SUBSCRIBER_SECTION ))) static _dy_message_pair _DY_MSG_UNIQUE_VAR = \
{\
&_DY_MSG_ASSOCIATE_PROTOCOL_METHOD(INDEX),\
&_DY_MSG_ASSOCIATE_LOGIC_METHOD,\
};

利用上述編譯注解的能力,搭配協(xié)議反射,就能達(dá)到在使用的時(shí)候,get 協(xié)議進(jìn)而讀取到存儲(chǔ)在 .data 段中的內(nèi)存地址來(lái)加載,這個(gè)能力也稱(chēng)為懶加載。

支持切面

核心思路如下(偽代碼),在注冊(cè)階段暴露出代碼塊模型,可以在塊中做類(lèi)似 AB 的邏輯切面。

isABTest = YES;

Register {
if (isABTest) {
return <ObjectABProtocol>ObjectA.new;
} else {
return <ObjectABProtocol>ObjectB.new;
}
}

循環(huán)引用

為了防止 subscriber 與 publisher 在 block 使用或者主類(lèi)與適配器的關(guān)聯(lián)情況下導(dǎo)致循環(huán)引用,適配器底層運(yùn)用了 NSProxy 來(lái)實(shí)現(xiàn)。如以下的 case 無(wú)需關(guān)心內(nèi)存不釋放問(wèn)題。

  • 場(chǎng)景例一
@implementation DYAudioViewForDOUYIN
RegisterAdapters(DYFeedInteractionControllerPrivateProtocol,DYFeedContaineAudioAdapter) {
if (GET_AB_TEST_CASE(enableAutoPlay)) {
return nil;
} else {
return [[DYAudioViewForDOUYIN alloc] init];
}
}

- (void)stopAudio:(BOOL)immediate
{
[[self weakTarget] refresh:^{
[[self weakTarget] refresh];
[self stop];
}];
}

- (void)stop
{
//do something
....
}

@end
  • 場(chǎng)景例二
@implementation DYFeedContainer

GetAdapters(DYFeedContaineAudioAdapter,DYFeedContaineVideoAdapter, DYFeedModuleConfig)

- (void)stopPlay
{
id <DYFeedContaineVideoAdapter> adapter = [self DYFeedContaineVideoAdapter];
[[self DYFeedContaineVideoAdapter] stopVideo:^{
[adapter refreshView];
}];

self.myBlock = ^(){
[adapter refreshView];
};
}

@end

綁定關(guān)聯(lián)

圖片

綁定關(guān)聯(lián)共分為兩部分,強(qiáng)關(guān)聯(lián)與弱關(guān)聯(lián)。

  • 強(qiáng)關(guān)聯(lián):將各適配器強(qiáng)綁定關(guān)聯(lián)在主類(lèi)上,這樣能實(shí)現(xiàn)適配器的生命周期跟隨主類(lèi)自動(dòng)釋放,在使用適配器對(duì)象時(shí)讓內(nèi)存持續(xù)處于最優(yōu)狀態(tài)。
  • 弱關(guān)聯(lián):將主類(lèi)弱關(guān)聯(lián)在適配器上,這樣能實(shí)現(xiàn)在隔離出來(lái)的附屬類(lèi)中,通過(guò) Key ( self = 適配器)拿到主類(lèi),達(dá)到反向通信的效果。

多語(yǔ)言適配

Swift 環(huán)境下不能在注冊(cè)階段友好的使用 attribute 編譯指令,去自定義段能力,要想高性能的使用懶注冊(cè)能力只能另辟蹊徑。

圖片

將注冊(cè)代碼塊直接放到 MachO 文件中的代碼區(qū),通過(guò)繼承協(xié)議 SwiftAdapter ,實(shí)現(xiàn)層實(shí)現(xiàn) + (id)lazyRegister 類(lèi)方法,runtime 的 Api 映射出 A 類(lèi)對(duì)象,在服務(wù)發(fā)現(xiàn)的階段來(lái)調(diào)用 A 類(lèi)方法代碼,這樣能解決“懶注冊(cè)”問(wèn)題;然后改造底層框架,控制內(nèi)部保證只會(huì)初始化一次,用戶(hù)視角無(wú)需關(guān)心。

E.g.

class ModuleADouYinLiteAdapter: NSObject,SwiftAdapterProtocol {
class func lazyRegister() -> NSObjectProtocol {
return ModuleADouYinLiteAdapter.init()
}
}

便利腳手架

在各語(yǔ)言環(huán)境對(duì)服務(wù)發(fā)現(xiàn)與注冊(cè)接口制造腳手架,使其用起來(lái)更加簡(jiǎn)便。

  1. Objective - C 宏

接口均用宏來(lái)封裝。

//服務(wù)注冊(cè)
RegisterAdapters(ModuleDouYinLiteAdapter) {
return ModuleDouYinLiteAdapter.new;
}
//服務(wù)發(fā)現(xiàn)
GetAdapters(ModuleDouYinLiteAdapter)
  1. SwiftProtocol 擴(kuò)展

Swift 環(huán)境下不能友好的使用宏封裝,此時(shí)我們可以通過(guò)對(duì) Protocol 進(jìn)行擴(kuò)展,以達(dá)到封裝效果。

//服務(wù)注冊(cè)
class func lazyRegister() -> NSObjectProtocol,ModuleDouYinLiteAdapterProtocol {
return ModuleDouYinLiteAdapter.init()
}
//服務(wù)發(fā)現(xiàn)
Protocol.getAdapter(self,ModuleDouYinLiteAdapterProtocol.self)

使用視角

OC編碼

共有接口差異代碼情景

服務(wù)注冊(cè)

圖片

圖片

圖片

?服務(wù)發(fā)現(xiàn)

圖片

Swift編碼

獨(dú)有接口差異代碼情景

服務(wù)注冊(cè)

  • 前置抽象協(xié)議接口,懶注冊(cè),支持切面。
  • 支持在各個(gè) Adapter 實(shí)現(xiàn)層中獲取 WeakTarget (主類(lèi))。

圖片

圖片

服務(wù)發(fā)現(xiàn)

圖片

輔助工具

就如很多人都喜歡玩的網(wǎng)游地下城與勇士( DNF ),輔助工具“連發(fā)”(顧名思義,連續(xù)發(fā)動(dòng),可以聯(lián)想到傳統(tǒng)單發(fā)步槍與自動(dòng)步槍的區(qū)別)不僅讓玩家節(jié)省了不少的按鍵成本,而且在連招上增強(qiáng)了打擊節(jié)奏感。同樣的道理,我們推薦使用 Xcode 自定義模板工具編程,讓使用者減少打出代碼的時(shí)間成本,在開(kāi)發(fā)中更加聚焦處理編碼邏輯。

圖片

使用規(guī)范

為讓開(kāi)發(fā)同學(xué)更加規(guī)范使用,我們?cè)诖a靜態(tài)檢查階段進(jìn)行代碼的攔截矯正,同時(shí)基于現(xiàn)狀列一下幾個(gè) Badcase 。

場(chǎng)景例一

  • 只進(jìn)行了分支判斷邏輯隔離,沒(méi)做到代碼隔離,這樣會(huì)將判斷邏輯帶到主類(lèi),使讓包大小增加。
// E.g. 錯(cuò)誤示例
- (void)masterFunction {
if ([self DYFeedAModuleLiteAdapter]) {
// lite code
} else {
// douyin or other Target code
}
}

//--------------------------------------------------------------------------

//E.g.正確示例
- (void)masterFunction {
[[self DYFeedAModuleAdapter] runFunction];
}

//各自Target實(shí)現(xiàn)runFunction協(xié)議方法
//in douyin
- (void)runFunction {
// code
}
//in Lite
- (void)runFunction {
// code
}

場(chǎng)景例二

  • 在同一個(gè)產(chǎn)物內(nèi),一個(gè)協(xié)議被多個(gè)類(lèi)實(shí)現(xiàn)( Debug 環(huán)境編譯階段會(huì)通過(guò)斷言進(jìn)行第一次攔截)。
// E.g. 錯(cuò)誤示例( douyin targer)
@interface AModuleAdapter<AModuleAdapter>
@interface BModuleAdapter<AModuleAdapter>

//E.g.正確示例(douyin targer)
@interface AModuleAdapter<AModuleAdapter>
@interface BModuleAdapter<BModuleAdapter>

場(chǎng)景例三

  • Adapter 方法在不同產(chǎn)品線下可能返回空值,如果想拿 Adapter 做 一些邏輯編碼,需要提前判斷是否為空。
// E.g. 錯(cuò)誤示例
- (DYAModuleFeedType)getType {
return [[self DYModuleAdapter] checkType];
}

//E.g.正確示例
- (DYAModuleFeedType)getType {
return [self DYModuleAdapter] ? [[self DYModuleAdapter] checkType]:/* 兜底邏輯 */;
}

生態(tài)建設(shè)

目前為止,多產(chǎn)物適配器框架實(shí)體 Adapter 已經(jīng)在抖音數(shù)個(gè)平臺(tái)業(yè)務(wù)線中批量使用,大部分 OC 業(yè)務(wù)場(chǎng)景均已覆蓋,而且 Swift 場(chǎng)景能力也已建設(shè)完畢,框架母體 ServiceKit 已接入 20 + 個(gè) App 。

寫(xiě)在最后

穩(wěn)扎穩(wěn)打

對(duì)于核心框架,我們寫(xiě)出的也許只有一行代碼,但是會(huì)有幾百萬(wàn)行甚至上千萬(wàn)行代碼會(huì)經(jīng)過(guò)它,一定要慎重思考。

責(zé)任編輯:未麗燕 來(lái)源: 字節(jié)跳動(dòng)技術(shù)團(tuán)隊(duì)
相關(guān)推薦

2024-04-12 14:42:21

Typescript渲染技術(shù)

2024-03-12 17:13:51

2022-12-22 08:51:40

vivo代碼

2022-06-06 12:19:08

抖音功耗優(yōu)化Android 應(yīng)用

2022-06-01 09:18:37

抖音ReDex算法優(yōu)化

2023-01-05 07:54:49

vivo故障定位

2023-10-27 12:16:23

游戲發(fā)行平臺(tái)SOP

2024-10-31 08:22:56

2024-06-13 17:10:16

2023-12-13 13:15:13

平臺(tái)開(kāi)發(fā)實(shí)踐

2023-03-28 08:28:34

2023-11-03 17:02:18

抖音直播畫(huà)質(zhì)優(yōu)化

2021-08-04 16:48:16

數(shù)字化

2022-07-20 22:55:39

直播OOM抖動(dòng)

2024-02-26 08:15:43

語(yǔ)言模型低代碼

2024-11-13 08:47:24

2024-12-05 12:01:09

2023-03-03 15:43:23

抖音世界杯畫(huà)質(zhì)優(yōu)化

2024-07-18 08:38:31

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 特黄一级 | 亚洲精品久久久久中文字幕欢迎你 | 日本成年免费网站 | 自拍偷拍亚洲视频 | 日韩伦理一区二区 | 色综合美女 | 日本激情一区二区 | 国产一区二区三区日韩 | av一区二区三区四区 | 三级免费 | 在线一区| 免费国产视频 | 久草综合在线视频 | 午夜性色a√在线视频观看9 | 91久久久久久久久久久久久 | 久久免费观看一级毛片 | 久久国产综合 | 人妖videosex高潮另类 | 天天久久 | 日本淫视频| 中文字幕在线观看 | av在线免费观看网站 | 在线观看国产精品视频 | 欧美成人精品一区 | 日韩欧美国产一区二区三区 | 久久骚| 99国产精品视频免费观看一公开 | 久久国产区 | 国产精品一区久久久 | 欧美久久国产 | 成人欧美一区二区三区色青冈 | 日本xx视频免费观看 | 欧美日韩在线一区二区 | 欧美日韩在线一区二区 | 亚洲高清av在线 | www.久久| 成人99| 黄色网址大全在线观看 | 国产成人精品一区二区三区四区 | 亚洲午夜视频 | 91精品国产自产精品男人的天堂 |