当前位置: 首页 > 科技观察

刚刚,阿里开源了iOS协程开发框架coobjc!

时间:2023-03-17 11:36:58 科技观察

刚刚,阿里巴巴正式开源了基于Apache2.0协议的协程开发框架coobjc,开发者可以在Github上下载。coobjc是iOS平台开源的协程开发框架,支持Objective-C和Swift,提供cokit库,为Foundation和UIKit中的部分API提供协程支持。本文将详细介绍coobjc的设计理念。和核心优势。开源地址https://github.com/alibaba/coobjciOS异步编程问题自2008年发布第一个iOS版本以来的11年里,iOS的异步编程方式发展缓慢。基于块的异步编程回调是目前iOS应用最广泛的异步编程方式。iOS系统提供的GCD库让异步开发变得简单方便。但是,基于这种编程方法也有很多缺点,主要有:容易进入“嵌套地狱”错误处理复杂冗长容易忘记调用完成处理程序条件执行变得非常困难独立调用的结果合并变得极其困难难以在错误的线程中继续执行(如子线程操作UI)难以定位多线程崩溃的原因(多线程崩溃占淘宝网购的60%以上)滥用锁导致卡死卡死而信号量对于多线程尤其是各种崩溃和性能问题,我们制定了很多编程规范,进行了各种新手培训,试图降低出现问题的概率,但问题依然严重。多线程导致的问题比例并没有明显下降。异步编程本质上是非常复杂的。训练很难从根本上解决问题,需要更好的编程方法来解决。解决方案上面的问题在很多系统和语言的开发中都可能会遇到。解决问题的标准方法是使用协程。C#、Kotlin、Python、Javascript等流行语言都支持协程和相关语法。使用这些语言进行开发读者可以很方便地使用协程和相关函数进行异步编程。2017年的C++标准开始支持协程,Swift5也包含了协程相关的标准。从目前的发展趋势来看,一种基于协程的异步编程新方法是我们解决现有异步编程问题的有效途径。但是Apple基本不会升级Objective-C,所以使用Objective-C的开发者无法使用官方的协程能力,***Swift的发布和推广还需要时间。读者可以快速享受协程带来的编程方式的改变。手淘架构团队基于长期对系统底层库和汇编的研究,通过汇编和C语言实现了对Objective-C和Swift协程***的支持。解决方案——coobjc。核心能力提供对类似于C#和Javascript语言的Async/Await编程方法的支持。通过调用协程中的await方法,可以同步获取异步方法的执行结果。非常适合IO、网络等异步耗时调用的同步顺序执行。改造。提供了类似Kotlin的Generator函数用于惰性计算生成序列化数据,非常适合多线程可中断序列化数据的生成和访问。提供Actor模型的实现。基于ActorModel,开发者可以开发更多线程安全的模块,避免直接调用函数导致的各种多线程崩溃。提供对元组的支持,Objective-C开发者可以通过元组享受类似于Python语言的多值返回的好处。内置系统扩展库,提供对NSArray、NSDictionary等容器库的协程扩展,解决序列化和反序列化过程中的异步调用问题。为NSData、NSString、UIImage等数据对象提供协程扩展,解决读写IO过程中的异步调用问题。提供NSURLConnection和NSURLSession的协程扩展,解决网络异步请求过程中的异步调用问题。提供NSKeyedArchieve、NSJSONSerialization等解析库的扩展,解决解析过程中异步调用的问题。coobjc设计的第??一层是协程内核,包括栈切换的管理、协程调度器的实现、协程间通信通道的实现等。中间层是对基于协程的算子的封装,目前支持async/await、Generator、Actor等编程模型。最上层是系统库的协程扩展,基本涵盖了Foundation和UIKit的所有IO和耗时方法。核心实现原理协程的核心思想是控制调用栈的主动投降和恢复。一般的协程实现都会提供两个重要的操作:Yield:表示放弃cpu,会中断当前的执行,回到上次Resume所在的地方。Resume:继续协程的运行。执行完Resume,回到上一个协程Yield所在的地方。当我们基于线程的代码执行时,是不可能进行暂停操作的。我们现在要做的是让代码执行暂停和恢复。基本上,代码执行是一个基于调用栈的模型,所以如果我们可以将状态保存在当前调用栈上,然后从缓存中恢复,那么我们就可以实现yield和resume。这个操作有多少种实现方式?第一种:使用glibc(云峰的库)的ucontext组件。第二种方法:使用汇编代码切换上下文(实现c协程),原理同ucontext。第三种方法:利用C语言语法switch-case的神奇技术实现(Protothreads)。第四种:使用C语言的setjmp和longjmp。第五:使用编译器支持语法糖。上述第三种和第四种只能用于跳转,不能在调用栈上保存状态。好像基本不能算是协程的实现,只能算是demo。官方不支持第五种,否则重写编译器的通用性很差。第一个解决方案的ucontext在iOS上已经过时,无法使用。然后我们使用第二种解决方案,用汇编模拟ucontext。模拟ucontext的核心是通过getContext和setContext来保存和恢复调用栈。需要熟悉不同CPU架构下的调用约定(CallingConvention)。汇编实现就是针对不同的CPU实现一套。我们目前实现了armv7、arm64、i386、x86_64,支持iPhone真机和模拟器。Showme代码说了这么多,我们来看看代码吧。让我们从一个简单的网络请求中加载图像功能,看看coobjc是如何使用的。下面是网络请求最常见的写法:下面是使用coobjc库修改后的代码:原本需要20行的代码经过coobjc改造后减少了一半,整个代码的逻辑性和可读性都得到了提升改善。更妙的是,这就是coobjc强大的能力,可以将原本复杂的异步代码,通过协程转换,变成逻辑简洁的顺序调用。coobjc还有许多其他强大的功能。本文不会过多介绍coobjc的实际使用。感兴趣的朋友可以去官方github仓库下载查看。性能提升我们在iPhone7iOS11.4.1设备上使用协程和传统的多线程方式模拟高并发数据读取场景。以下是两种方式得到的压力测试数据。测试机:iPhone7iOS11.4.1数据文件大小:20M最大协程使用线程数:4数据测试结果(统计为所有并发访问结束时的总耗时):从上表可以看出使用量很大在小场景下,由于多线程可以充分利用设备的计算核心,coobjc的总耗时比传统多线程略高,但是由于整体耗时很小,区别并不明显,但是随着并发量的增加,coobjc的优势开始逐渐体现出来。当并发量超过1000时,传统的多线程开始出现线程分配异常,导致很多并发任务无法执行。所以上表显示是20多秒,其实是一个任务。已经不能正常执行了,但是coobjc还是可以正常运行的。我们在手机淘宝这样的超级APP中尝试了协程改造。对于一些性能较差的页面,我们发现滑动过程中主线程IO调用和数据分析较多,导致帧率下降严重。通过引入coobjc,在不改变原有业务代码的基础上,通过IO的globalhook部分和数据分析方法,可以在不影响原有业务逻辑的情况下,将原本在主线程中同步执行的IO方法异步执行.低端机(iPhone6及以下)帧率提升20%左右。优点是简洁,概念少:操作符只有几个,不能比响应式风格的几十个操作符简单。原理简单:协程的实现原理非常简单,整个协程库只有几千行代码。使用方便:比GCD使用简单,接口少。易于改造:现有代码只需要少量改动即可转为协程,我们为系统库提供了大量的协程接口。异步逻辑清晰的同步编写:以同步顺序的方式编写代码是人类最容易接受的方式,可以大大降低出错的概率。可读性高:使用协程编写的代码比块嵌套编写的代码更具可读性。性能调度性能更快:协程本身不需要切换内核级线程,调度性能快,即使创建上万个协程也毫无压力。减少冻结和冻结:协程的使用有助于开发减少锁和信号量的滥用。通过封装IO等引起阻塞的协程接口,可以从根本上减少卡顿和死机,提高应用的整体性能。总结程序是为人类阅读而编写的,只是偶尔由机器执行。——Abelson和Sussman的基于协程的编程范式可以帮助开发者编写出更漂亮、更健壮、更具可读性的代码。协程可以帮助我们在编写并发代码的过程中减少线程和锁的使用,提高应用程序的性能和稳定性。【本文为专栏作者《阿里巴巴官方技术》原创稿件,转载请联系原作者】点此查看作者更多好文