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

刚刚,阿里开源 iOS 协程开发框架 coobjc!,第1张

阿里妹导读:刚刚,阿里巴巴正式对外开源了基于 Apache 2.0 协议的协程开发框架 coobjc,开发者们可以在 Github 上自主下载。

coobjc是为iOS平台打造的开源协程开发框架,支持Objective-C和Swift,同时提供了cokit库为Foundation和UIKit中的部分API提供了 协程 化支持,本文将为大家详细介绍coobjc的设计理念及核心优势。

从2008年第一个iOS版本发布至今的11年时间里,iOS的异步编程方式发展缓慢。

基于 Block 的异步编程回调是目前 iOS 使用最广泛的异步编程方式,iOS 系统提供的 GCD 库让异步开发变得很简单方便,但是基于这种编程方式的缺点也有很多,主要有以下几点:

针对多线程以及尤其引发的各种崩溃和性能问题,我们制定了很多编程规范、进行了各种新人培训,尝试降低问题发生的概率,但是问题依然很严峻,多线程引发的问题占比并没有明显的下降,异步编程本来就是很复杂的事情,单靠规范和培训是难以从根本上解决问题的,需要有更加好的编程方式来解决。

上述问题在很多系统和语言开发中都可能会碰到,解决问题的标准方式就是使用协程,C#、Kotlin、Python、Javascript 等热门语言均支持协程极其相关语法,使用这些语言的开发者可以很方便的使用协程及相关功能进行异步编程。

2017 年的 C++ 标准开始支持协程,Swift5 中也包含了协程相关的标准,从现在的发展趋势看基于协程的全新的异步编程方式,是我们解决现有异步编程问题的有效的方式,但是苹果基本已经不会升级 Objective-C 了,因此使用Objective-C的开发者是无法使用官方的协程能力的,而最新 Swift 的发布和推广也还需要时日,为了让广大iOS开发者能快速享受到协程带来的编程方式上的改变,手机淘宝架构团队基于长期对系统底层库和汇编的研究,通过汇编和C语言实现了支持 Objective-C 和 Swift 协程的完美解决方案 —— coobjc。

核心能力

内置系统扩展库

coobjc设计

最底层是协程内核,包含了栈切换的管理、协程调度器的实现、协程间通信channel的实现等。

中间层是基于协程的 *** 作符的包装,目前支持async/await、Generator、Actor等编程模型。

最上层是对系统库的协程化扩展,目前基本上覆盖了Foundation和UIKit的所有IO和耗时方法。

核心实现原理

协程的核心思想是控制调用栈的主动让出和恢复。一般的协程实现都会提供两个重要的 *** 作:

我们基于线程的代码执行时候,是没法做出暂停 *** 作的,我们现在要做的事情就是要代码执行能够暂停,还能够再恢复。 基本上代码执行都是一种基于调用栈的模型,所以如果我们能把当前调用栈上的状态都保存下来,然后再能从缓存中恢复,那我们就能够实现yield和 resume。

实现这样 *** 作有几种方法呢?

上述第三种和第四种只是能过做到跳转,但是没法保存调用栈上的状态,看起来基本上不能算是实现了协程,只能算做做demo,第五种除非官方支持,否则自行改写编译器通用性很差。而第一种方案的 ucontext 在iOS上是废弃了的,不能使用。那么我们使用的是第二种方案,自己用汇编模拟一下 ucontext。

模拟ucontext的核心是通过getContext和setContext实现保存和恢复调用栈。需要熟悉不同CPU架构下的调用约定(Calling Convention). 汇编实现就是要针对不同cpu实现一套,我们目前实现了 armv7、arm64、i386、x86_64,支持iPhone真机和模拟器。

说了这么多,还是看看代码吧,我们从一个简单的网络请求加载图片功能来看看coobjc到底是如何使用的。

下面是最普通的网络请求的写法:

下面是使用coobjc库协程化改造后的代码:

原本需要20行的代码,通过coobjc协程化改造后,减少了一半,整个代码逻辑和可读性都更加好,这就是coobjc强大的能力,能把原本很复杂的异步代码,通过协程化改造,转变成逻辑简洁的顺序调用。

coobjc还有很多其他强大的能力,本文对于coobjc的实际使用就不过多介绍了,感兴趣的朋友可以去官方github仓库自行下载查看。

我们在iPhone7 iOS11.4.1的设备上使用协程和传统多线程方式分别模拟高并发读取数据的场景,下面是两种方式得到的压测数据。

从上面的表格我们可以看到使用在并发量很小的场景,由于多线程可以完全使用设备的计算核心,因此coobjc总耗时要比传统多线程略高,但是由于整体耗时都很小,因此差异并不明显,但是随着并发量的增大,coobjc的优势开始逐渐体现出来,当并发量超过1000以后,传统多线程开始出现线程分配异常,而导致很多并发任务并没有执行,因此在上表中显示的是大于20秒,实际是任务已经无法正常执行了,但是coobjc仍然可以正常运行。

我们在手机淘宝这种超级App中尝试了协程化改造,针对部分性能差的页面,我们发现在滑动过程中存在很多主线程IO调用、数据解析,导致帧率下降严重,通过引入coobjc,在不改变原有业务代码的基础上,通过全局hook部分IO、数据解析方法,即可让原来在主线程中同步执行的IO方法异步执行,并且不影响原有的业务逻辑,通过测试验证,这样的改造在低端机(iPhone6及以下的机器)上的帧率有20%左右的提升。

简明

易用

清晰

性能

程序是写来给人读的,只会偶尔让机器执行一下。——Abelson and Sussman

基于协程实现的编程范式能够帮助开发者编写出更加优美、健壮、可读性更强的代码。

协程可以帮助我们在编写并发代码的过程中减少线程和锁的使用,提升应用的性能和稳定性。

本文作者:淘宝技术

CocoaLumberjack是Mac和iOS上一个集快捷、简单、强大和灵活于一身的日志框架。CocoaLumberjack类似于流行 的日志框架(如log4j),但它是专为Objective-C设计的,利用了多线程、GCD(如果可用)、无锁原子 *** 作Objective-C运行时的 动态特性。

快速

在大多数用例中,Lumberjack比NSLog快了一个数量级。

简单

当应用程序启动时,只需一行加单的代码就可配置Lumberjack。然后用DDLog语句简单地取代NSLog语句。 并且DDLog宏与NSLog的有完全相同的格式和语法,所以超级简单。

强大

一个日志语句可以被发送到多个logger,意味着你可以同时记录文件和控制台。此外,还可以创建自己的logger,将日志语句发送到网络、数据库或者分布式文件系统中。没有任何限制。

灵活性

配置你自己想要的日志框架。修改每个文件的日志级别(尤其是测试时)。修改每个logger的日志级 别(详细的控制台,但是简洁的日志文件)。修改每个Xcode配置的日志级别。为你的应用程序定制日志级别的数量。添加自己的精细的日志。在运行时动态修 改日志级别。 选择如何以及何时回滚你的日志文件。将日志文件上传至中心服务器。压缩存档日志文件来节省硬盘空间。

当你遇到一下情况是,你可以选择Lumberjack框架:

1.你想找到一种方式来跟踪在程序中不断出现的不可复制的bug

2.你对iPhone上的简短日志感到很失望

3.出于支持系统和稳定性的需要,你想将应用程序升级到下一级别

4.为你的应用程序(Mac或者iPhone)寻找企业级的日志解决方案。

如何开始使用Lumberjack框架

开始

三步开始使用CocoaLumberjack框架:

1.将Lumberjack文件添加到你的项目中

2.配置框架

3.将NSLog指令转换为使用Lumberjack宏指令

把Lumberjack框架添加到你的项目

需要添加的主要文件有四个:

1.@DDLog(整个框架的基础)

2.@DDASLLogger(发送日志语句到苹果的日志系统,以便它们显示在Console.app上)

3.@DDTTYLoyger(发送日志语句到Xcode控制台,如果可用)

4.@DDFIleLoger(把日志语句发送至文件)

DDLog是强制性的,其余的都是可选的,这取决于你打算如何使用这个框架。例如,如果你不打算纪录到一个文件,你可以跳过DDFileLogger,或者你想跳过ASL以便更快的文件记录,你可以跳过DDASLLoger。

配置框架

首先,你想要在你的应用程序中配置这个日志框架,通常在applicationDidFinishLaunching方法中配置。

开始时,你需要下面两行代码:

[DDLog addLogger:[DDASLLogger sharedInstance]]

[DDLog addLogger:[DDTTYLogger sharedInstance]]

这将在你的日志框架中添加两个“logger”。也就是说你的日志语句将被发送到Console.app和Xcode控制 台(就像标准的NSLog)

这个框架的好处之一就是它的'灵活性,如果你还想要你的日志语句写入到一个文件中,你可以添加和配置一个file logger:

fileLogger = [[DDFileLogger alloc] init]

fileLogger.rollingFrequency = 60 * 60 * 24// 24 hour rolling

fileLogger.logFileManager.maximumNumberOfLogFiles = 7

[DDLog addLogger:fileLogger]

上面的代码告诉应用程序要在系统上保持一周的日志文件。

用DDLog替换NSLog语句

DDLog的头文件定义了你用来替换NSLog语句的宏,本质上看起来向下边这样:

// Convert from this:

NSLog(@"Broken sprocket detected!")

NSLog(@"User selected file:%@ withSize:%u", filePath, fileSize)

// To this:

DDLogError(@"Broken sprocket detected!")

DDLogVerbose(@"User selected file:%@ withSize:%u", filePath, fileSize)

我们看到DDLog宏和NSLog的语法完全相同。

所以你所要做的就是决定每个NSlog语句属于哪种日志级别。DDLog默认有四种级别的日志,分别是:

1.@DDlogError

2.@DDlogWarn

3.@DDlogInfo

4.@DDlogVerbose

(注意:你也可以自定义级别以及级别名或者添加更精细的控制来代替系统四个简单的等级。)

当然选择哪个NSLog语句取决于你的消息的严重程度。

下面的这些不同的日志等级也许正有你所需要的:

1.如果你将日志级别设置为 LOG_LEVEL_ERROR,那么你只会看到DDlogError语句。

2.如果你将日志级别设置为LOG_LEVEL_WARN,那么你只会看到DDLogError和DDLogWarn语句。

3.如果您将日志级别设置为 LOG_LEVEL_INFO,那么你会看到error、Warn和Info语句。

4.如果您将日志级别设置为LOG_LEVEL_VERBOSE,那么你会看到所有DDLog语句。

5.如果您将日志级别设置为 LOG_LEVEL_OFF,你将不会看到任何DDLog语句。

那么我在哪里设置日志级别呢,在整个项目中我只能使用一个日志级别吗?

当然不是,我们都知道正如调试或者添加新特性,如果你想详细纪录目前正在做的那部分,Lumberjack框 架提供了对每个文件的调试控制,你仅可以修改编辑中的文件的日志级别。

(注释:当然还有许多其他高级选项,比如全球日志级别,Xcode的每个配置级别,每个logger级别等,我们将在另一篇文章中讲到)。

以下是如何转换你的日志语句:

// CONVERT FROM THIS

#import "Sprocket.h"

@implementation Sprocket

- (void)someMethod

{

NSLog(@"Meet George Jetson")

}

@end

// TO THIS

#import "Sprocket.h"

#import "DDLog.h"

static const int ddLogLevel = LOG_LEVEL_VERBOSE

@implementation Sprocket

- (void)someMethod

{

DDLogVerbose(@"Meet George Jetson")

}

@end

注意日志级别声明为常量,这意味着这意味着日志级别阈值以上的DDLog语句都将编译到你的项目中。

自动引用计数(ARC)

最新版本的Lumberjack使用ARC。如果你的项目没有使用ARC,你可以在ARC页面学习如何在Xcode中像ARC一样正确地标记Lumberjack文件。


欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/yw/11719157.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-18
下一篇 2023-05-18

发表评论

登录后才能评论

评论列表(0条)

保存