连发程序(游戏连发辅助)

前言介绍在软件架构领域,框架的功能类似于基础设施服务,是为实现一个行业标准而形成的组件规范。简单来说,框架就是制定一套规范或规则,培养学生在规范或规则下工作。本

前言介绍

在软件架构领域,框架的功能类似于基础设施服务,是为实现一个行业标准而形成的组件规范。简单来说,框架就是制定一套规范或规则,培养学生在规范或规则下工作。本文通过分析框架实体ServiceKit/Adapter,探讨其底层结构和架构设计。

背景描述

随着Tik Tok业务的发展,为了保证整体项目演进和迭代计划的高效运行,系统化建设已经提上日程,Codebase(一般称为产品)的集成就是其中一个项目。该项目主要为开发生提供底层复用能力,提升R&D团队的效率,致力于帮助开发生轻松高效地开发和管理代码。

连发程序(游戏连发辅助)插图

在代码趋同的过程中,技术团队在各个业务线方向进行差异化探索;在演进过程中,业务线之间的耦合越来越强,开发生迫切需要一套解决方案来做差异化的代码隔离。下图显示了Tik Tok和Tik Tok至尊版模块之间的差异。

连发程序(游戏连发辅助)插图(1)

回过头来看,在过去的开发中,开发者一般都是用isLite或者isPad来区分不同产品的差异,但是这种方法严重破坏了整个Tik Tok项目的架构体系。以下从几个维度进行分析。

研发效率:需要支持不同宏变量进行 lint ,有重复 lint ,单个组件很难区分项目控制二进制发版频率,二进制需要频繁更新,宏会导致很多混编二进制,影响编译效率,如果以单个文件作为编译缓存单元,宏隔离也会降低编译缓存命中率。可扩展性:扩展性差,缺乏动态能力和插件能力,添加新功能和修改原有功能会导致类实现的代码急剧膨胀。圈复杂度:宏隔离的代码分散,修改和重构成本高。组件粒度:无法支持项目间差异业务独立成组件,背离高内聚、低耦合原则。

我们的目标和愿景是制定一套符合Tik Tok工程架构体系,高效、通用、便捷的框架规范,让开发者在标准规则下编码。

架构设计

启蒙绘画

开悟是开始做事之前的抽象意识。如下图所示,在多产品的R&D环境下,通用代码可以高效重用,差异化代码可以优雅分离。

连发程序(游戏连发辅助)插图(2)

为了帮助新同学快速上手框架,笔者在搭建这个框架Swift的过程中,根据最近的几个项目经验,总结了一套系统的脑图。下面是框架系统化的全景图。

全景思维

内容比较多,但是全景思维还是想在这里提一下,说不定哪个阶段会给你启发;建议从树的根节点开始,有选择地了解。想大概了解一下,只需要到3级左右就可以了。如果想更深入的了解,请到叶子节点(为了不影响阅读体验,更详细的节点已经被剪掉了)。

连发程序(游戏连发辅助)插图(3)

基于以上框架的系统化思路,全章会先介绍一些设计思路,再进行性能等相关技术细节。由于篇幅有限,我们将把重点放在我们认为比较重要的技术点上。

设计思想

适配器模式

在设计模式中,适配器模式有时被称为打包样式或打包。把一个类的接口转移到用户所期望的。通过将类自己的接口包装在一个现有的类中,改编使得由于接口不兼容而不能一起工作的类能够一起工作。

-维基百科适配器模式

连发程序(游戏连发辅助)插图(4)

开发生不需要关心每个模块的复杂程度,业务的逻辑,选择类对象还是实例对象,如何初始化各自的单元等等。,而只需要基于封装好的适配器自己做任务调度,类似于万能充电器(90后一代的产物: >: ),无需关注电池是华为的还是OPPO的,即插即用。

注册和发现

注册服务发现思想

服务演进

下面三张图简单描述了web服务时代从传统服务到微服务时代的过程(传统服务->:并发服务->:分布式微服务)。有兴趣可以了解一下,这里就不多介绍了。

连发程序(游戏连发辅助)插图(5)

连发程序(游戏连发辅助)插图(6)

连发程序(游戏连发辅助)插图(7)

微服务

微服务是一种以业务功能为中心的服务设计理念。每个服务都有自己的业务功能,其API对外开放,没有语言限制。应用程序由一个或多个微服务组成。

——维基百科,微服务

了解了简单的微服务后,从服务的角度来说,多个目标产品可以根据每个业务模块划分为多个适配器服务,通过绑定多个适配器协议,可以达到一对多的效果。

下面深入介绍一下内部的设计思路。在使用阶段,一个主类可以向多个适配器类发送消息;在注册过程中,一个适配器类可以绑定多个适配器协议,满足两种场景:一是多个产品必须实现的接口可以放在一个公共协议上;第二,单个产品必须实现的接口可以放在一个独立的协议上,通用协议+独立协议可以组合在一起,由同一个有上下文的适配器类实现。

连发程序(游戏连发辅助)插图(8)

说到微服务,我们必须了解下两个概念,服务注册和发现。

服务注册

服务注册:是将提供某个服务的模块信息注册到一个公共的组件上去。(如下示例代码更加容易理解)

//服务注册service kit . register(moduleserver);服务发现

服务发现:是指使用一个注册中心来记录分布式系统中的全部服务的信息,以便其他服务能够快速的找到这些已注册的服务;不管是服务新增和服务删减都能实现自动发现。(如下示例代码更加容易理解)

//服务发现ServiceKit.get(AModuleServer); 进阶图纸

连发程序(游戏连发辅助)插图(9)//服务发现service kit . get(AModuleServer);高级绘图

蓝色框:抖音 Target黑色框:抖音极速版 TargetaXXXDOUYINAdapter:是 XXXDOUYINAdapterImpl 的服务实例。XXXDOUYINAdapterImpl:是订阅者,发布者是持有 XXXDOUYINAdapterImpl 实例 XXXDOUYINAdapter 的主类。<>XXXDOUYINAdapter:面向协议编程,抽象 Protocol 接口,抽离各自差异性、公共性代码的接口。

上图进一步总结了整个项目的背景(Tik Tok和Tik Tok至尊版两套代码,有重复也有差异,如何继续共享重复的代码,并将不同的代码隔离到各自的目标产品中,以免耦合),我们要做的过程(通过适配器模式调度任务,为协议编程,将共享的和不同的代码分离为接口,在各自的目标中实现各自的协议impl),以及我们实现了什么。

关系图纸

连发程序(游戏连发辅助)插图(10)

工程视角

从Tik Tok现有工程框架的角度理解设计。

连发程序(游戏连发辅助)插图(11)

流程实战

接下来,我们来一个程序性的实践练习。

在实际的代码实践中,订阅类在App内存中创建一个实例,订阅者的生命周期由所有关联的发布者决定,比如多个控制器将嵌入式逻辑汇总到一个处理器,或者一个父控制器对应多个子控制器。

连发程序(游戏连发辅助)插图(12)

技术细节

在了解了以上设计图之后,我们深入浅出的分析一下内部的技术细节。

编译并插入

常规思维下,注册会被放入App的启动阶段,但是很容易拖慢App的启动速度。为了在不影响启动速度的情况下尽早注册,需要基于编译器特性:_ _ attribute _ _(section(& # 34;姓名& # 34;)))实现,通过属性指令,编译时写在。数据段,然后在运行时读出。下图介绍了编译注释的简单过程。

连发程序(游戏连发辅助)插图(13)

代码示例

__attribute((used,section(_ DY _ SEGMENT & # 34;,"_ DY _ MSG _ ASSOCIATE _ SUBSCRIBER _ SECTION)))static _ DY _ message _ pair _ DY _ MSG _ UNIQUE _ VAR = \ { \ & _ DY _ MSG _ ASSOCIATE _ PROTOCOL _ METHOD(INDEX),\ & _ DY _ MSG _ ASSOCIATE _ LOGIC _ METHOD,\ };使用上述编译注释的能力,结合协议反射,get协议可以读取存储在。要加载的数据节。这种能力也叫做懒加载。

支持部分

核心思想如下(伪代码)。注册阶段暴露代码块模型,可以在块中做出类似AB的逻辑段。

isABTest = YES注册{ if(isab test){ return & lt;ObjectABProtocol & gtObjectA.new} else { return & ltObjectABProtocol & gtObjectB.new}}循环引用

当使用block或者主类与适配器关联时,为了防止订阅者和发布者之间的循环引用,在适配器的底层使用了NSProxy。例如,下面的情况不需要关心内存不释放。

场景例一

@ implementation dyaudioviewfordouyinregistadapters(DYFeedInteractionControllerPrivateProtocol,DYFeedContaineAudioAdapter){ if(GET _ AB _ TEST _ CASE(enableAutoPlay)){ return nil;} else { return[[DYAudioViewForDOUYIN alloc]init];} }-(void)stop audio:(bool)immediate {[[self weak target]refresh:^{[[self weak target]refresh];【自停】;}];}-(void) stop {//dosomething...} @ end场景示例2 @实现DyFeedcontaineGetadapters(dyfeedcontaineaudioadapter,DyFeedcontaineVideoAdapter,DyFeedModeleConfig)-(void)stop play { id < DyFeedcontaineVideoAdapter & gt;adapter =[self DYFeedContaineVideoAdapter];[[self dyfeedcontainevideoadapter]stopvideo:^{[adapter refresh view];}];self . my block = ^(){[适配器刷新视图];};} @结束绑定关联

连发程序(游戏连发辅助)插图(14)

绑定分为两部分,强关联和弱关联。

强关联:将各适配器强绑定关联在主类上,这样能实现适配器的生命周期跟随主类自动释放,在使用适配器对象时让内存持续处于最优状态。弱关联:将主类弱关联在适配器上,这样能实现在隔离出来的附属类中,通过 Key ( self = 适配器)拿到主类,达到反向通信的效果。

多语言适应

在Swift环境下,你不能在注册阶段使用属性编译指令友好地定制分段能力。如果想用高性能的懒人注册能力,只能另辟蹊径。

连发程序(游戏连发辅助)插图(15)

注册代码块直接放在MachO文件的代码区,通过继承协议SwiftAdapter在实现层实现+(id)lazyRegister类方法。运行时的Api在服务发现阶段映射A类对象,调用A类方法代码,可以解决“懒注册”的问题。然后改造底层框架,控制内部保证只会初始化一次,用户的视角不需要关心。

例如

class ModuleADouYinLiteAdapter:ns object,SwiftAdapterProtocol { class func lazy register()-& gt;NSObjectProtocol { return moduleadouyinliteadapter . init()} }方便搭建为各种语言环境下的服务发现和注册接口做搭建,更容易使用。

Objective - C 宏

所有接口都由宏封装。

//服务注册register adapters(moduledouyinliteadapter){ return moduledouyinliteadapter . new;}//Service discovery Get Adapters(moduledouinlitadapter)Swift协议扩展在Swift环境下,宏封装无法友好使用。这时候我们可以扩展协议,达到封装的效果。

//服务注册class func lazy register()-->;NSObjectProtocol,moduleduyliteaprotocol { return moduleduyliteaprotocol . init()}//服务发现协议. getadapter (self,ModuleDuyLiteaPteroprotocol.self)使用视角OC编码。

通用接口差异代码场景

服务注册

连发程序(游戏连发辅助)插图(16)

连发程序(游戏连发辅助)插图(17)

连发程序(游戏连发辅助)插图(18)

服务发现

连发程序(游戏连发辅助)插图(19)

代码Swift

唯一接口差异代码场景

服务注册

前置抽象协议接口,懒注册,支持切面。支持在各个 Adapter 实现层中获取 WeakTarget (主类)。

连发程序(游戏连发辅助)插图(20)

连发程序(游戏连发辅助)插图(21)

服务发现

连发程序(游戏连发辅助)插图(22)

辅助工具

就像很多人喜欢玩的网游《地下城与勇士》(DNF)一样,辅助工具“连发”(顾名思义,连发可以联想到传统单发步枪和自动步枪的区别)不仅为玩家节省了大量的关键成本,还在连发中增强了打击的节奏感。同样的道理,我们推荐使用Xcode自定义模板工具进行编程,这样用户可以减少键入代码的时间成本,在开发中更加关注编码逻辑的处理。

连发程序(游戏连发辅助)插图(23)

使用规范

为了让开发学生更规范的使用,我们修正了静态检查阶段代码的截取,并根据现状列举了几个Badcase。

场景1

只进行了分支判断逻辑隔离,没做到代码隔离,这样会将判断逻辑带到主类,使让包大小增加。

//例如错误示例——(void)master函数{ if([self dyfeedamoduleliteadapter]){//lite code } else {//dou yin或其他目标代码}}// -。}//各目标//在douyin-(void) run函数{//code}/在lite-(void) run函数{//code }中实现runFunction协议的方法场景示例2

在同一个产物内,一个协议被多个类实现( Debug 环境编译阶段会通过断言进行第一次拦截)。

//例如错误示例(douyin targer) @接口模块适配器< AModuleAdapter & gt@ interface BModuleAdapter & ltAModuleAdapter & gt///例如正确示例(douyin targer) @接口模块适配器< AModuleAdapter & gt@ interface BModuleAdapter & ltBModuleAdapter & gt场景3

Adapter 方法在不同产品线下可能返回空值,如果想拿 Adapter 做 一些逻辑编码,需要提前判断是否为空。

//例如错误示例-(dyamoudefeedtype)GetType { return[[selfdymoduleadapter]check type];}//例如正确示例-(dyamoudefeedtype)GetType { return[selfdyModuleAdapter]?[[self dymoduleadapter]检查类型]:/*底层逻辑*/;}生态建设截至目前,多产品适配器框架实体适配器已在Tik Tok多个平台业务线批量使用,覆盖了大部分OC业务场景,同时也构建了Swift场景能力,框架母ServiceKit已接入20+app。

写在最后

立场坚定,稳扎稳打

对于核心框架,我们可能只写一行代码,但是会有几百万甚至几千万行代码要经过它,所以一定要仔细思考。

连发程序(游戏连发辅助)插图(24)

加入我们

我们是负责Tik Tok客户基础能力研发和新技术探索的团队。深耕工程/业务架构、R&D工具、R&D平台、编译系统等方向,支持业务快速迭代,同时保证超大规模团队的R&D效率和工程质量。不断探索性能/稳定性/高可用性,努力为全球数亿用户提供极致的基础体验。同时也在推动Swift/Swift ui/intelligent/automatic技术在复杂项目中的应用,为研发提供最前沿的开发经验,具体可参考:简单谈谈Tik Tok iOS基础技术有哪些岗位适合你

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。

作者:美站资讯,如若转载,请注明出处:https://www.meizw.com/n/103004.html

发表回复

登录后才能评论