From aafb6879593306b3adda42fbd8b5687e980bf8d5 Mon Sep 17 00:00:00 2001 From: iiiceblink <1248800415@qq.com> Date: Thu, 28 Feb 2019 00:10:07 +0800 Subject: [PATCH] Update READ_cn.md --- README_cn.md | 203 ++++++++++++++++++++++++++++----------------------- 1 file changed, 110 insertions(+), 93 deletions(-) diff --git a/README_cn.md b/README_cn.md index f4c2a48..617ded3 100644 --- a/README_cn.md +++ b/README_cn.md @@ -2,14 +2,15 @@ coobjc

-这个库为Objective-C和Swift提供了协程功能。coobjc支持await、generator和actor model,接口参考了C#、Javascript和Kotlin中的很多设计。我们还提供了[cokit库](cokit/README.md)为Foundation和UIKit中的部分API提供了协程化支持,包括NSFileManager, JSON, NSData, UIImage等。coobjc也提供了元组的支持。 +这个库为 Objective-C 和 Swift 提供了协程功能。coobjc 支持 await、generator 和 actor model,接口参考了 C# 、Javascript 和 Kotlin 中的很多设计。我们还提供了[cokit库](cokit/README.md)为 Foundation 和 UIKit 中的部分 API 提供了协程化支持,包括 NSFileManager , JSON , NSData , UIImage 等。coobjc 也提供了元组的支持。 ## 0x0 iOS异步编程问题 -基于Block的异步编程回调是目前iOS使用最广泛的异步编程方式,iOS系统提供的GCD库让异步开发变得很简单方便,但是基于这种编程方式的缺点也有很多,主要有以下几点: + +基于 Block 的异步编程回调是目前 iOS 使用最广泛的异步编程方式,iOS 系统提供的 GCD 库让异步开发变得很简单方便,但是基于这种编程方式的缺点也有很多,主要有以下几点: * 容易进入"嵌套地狱" * 错误处理复杂和冗长 -* 容易忘记调用completion handler +* 容易忘记调用 completion handler * 条件执行变得很困难 * 从互相独立的调用中组合返回结果变得极其困难 * 在错误的线程中继续执行 @@ -20,9 +21,10 @@ ## 0x1 解决方案 -上述问题在很多系统和语言中都会遇到,解决问题的标准方式就是使用协程。这里不介绍太多的理论,简单说协程就是对基础函数的扩展,可以让函数异步执行的时候挂起然后返回值。协程可以用来实现generator,异步模型以及其他强大的能力。 -Kotlin是这两年由JetBrains推出的支持现代多平台应用的静态编程语言,支持JVM,Javascript,目前也可以在iOS上执行,这两年在开发者社区中也是比较火。
在Kotlin语言中基于协程的async/await,generator/yield等异步化技术都已经成了语法标配,Kotlin协程相关的介绍,大家可以参考:[https://www.kotlincn.net/docs/reference/coroutines/basics.html](https://www.kotlincn.net/docs/reference/coroutines/basics.html) +上述问题在很多系统和语言中都会遇到,解决问题的标准方式就是使用协程。这里不介绍太多的理论,简单说协程就是对基础函数的扩展,可以让函数异步执行的时候挂起然后返回值。协程可以用来实现 generator ,异步模型以及其他强大的能力。 + +Kotlin 是这两年由 JetBrains 推出的支持现代多平台应用的静态编程语言,支持 JVM ,Javascript ,目前也可以在 iOS 上执行,这两年在开发者社区中也是比较火。
在 Kotlin 语言中基于协程的 async/await ,generator/yield 等异步化技术都已经成了语法标配,Kotlin 协程相关的介绍,大家可以参考:[https://www.kotlincn.net/docs/reference/coroutines/basics.html](https://www.kotlincn.net/docs/reference/coroutines/basics.html) ## 0x2 协程 @@ -32,39 +34,42 @@ Kotlin是这两年由JetBrains推出的支持现代多平台应用的静态编 协程的概念在60年代就已经提出,目前在服务端中应用比较广泛,在高并发场景下使用极其合适,可以极大降低单机的线程数,提升单机的连接和处理能力,但是在移动研发中,iOS和android目前都不支持协程的使用 ## 0x3 coobjc framework -coobjc是由手机淘宝架构团队推出的能在iOS上使用的协程开发框架,目前支持Objective-C和Swift中使用,我们底层使用汇编和C语言进行开发,上层进行提供了Objective-C和Swift的接口,目前以Apache开源协议进行了开源. -### 0x31 Install +coobjc 是由手机淘宝架构团队推出的能在 iOS 上使用的协程开发框架,目前支持 Objective-C 和 Swift 中使用,我们底层使用汇编和 C 语言进行开发,上层进行提供了 Objective-C 和 Swift 的接口,目前以 Apache 开源协议进行了开源. + +### 0x31 安装 + * cocoapods:  pod 'coobjc' -* source code: 所有代码在 ./coobjc 目录下 +* 源码安装: 所有代码在 ./coobjc 目录下 -### 0x32 Documents -* Read the [协程框架设计](docs/arch_design.md) document. -* Read the [coobjc Objective-C Guide](docs/usage.md) document. -* Read the [coobjc Swift Guide](docs/usage_swift.md) document. -* Read the [cokit framework](cokit/README.md) document, learn how to use the wrapper api of System Interface. +### 0x32 文档 -### 0x33 Features +* 阅读 [协程框架设计](docs/arch_design.md) 文档。 +* 阅读 [coobjc Objective-C Guide](docs/usage.md) 文档。 +* 阅读 [coobjc Swift Guide](docs/usage_swift.md) 文档。 +* 阅读 [cokit framework](cokit/README.md) 文档, 学习如何使用系统接口封装的 api 。 + +### 0x33 特性 #### async/await -* create coroutine +* 创建协程 -使用co_launch方法创建协程 -``` +使用 `co_launch` 方法创建协程 + +```objc co_launch(^{ ... }); ``` -co_launch创建的协程默认在当前线程进行调度 +`co_launch` 创建的协程默认在当前线程进行调度 -* await异步方法 +* await 异步方法 -在协程中我们使用await方法等待异步方法执行结束,得到异步执行结果 - -``` +在协程中我们使用 await 方法等待异步方法执行结束,得到异步执行结果 +```objc - (void)viewDidLoad{ ... co_launch(^{ @@ -75,12 +80,13 @@ co_launch创建的协程默认在当前线程进行调度 } ``` -上述代码将原本需要dispatch_async两次的代码变成了顺序执行,代码更加简洁 +上述代码将原本需要 `dispatch_async` 两次的代码变成了顺序执行,代码更加简洁 * 错误处理 -在协程中,我们所有的方法都是直接返回值的,并没有返回错误,我们在执行过程中的错误是通过co_getError()获取的,比如我们有以下从网络获取数据的接口,在失败的时候,promise会reject:error

-``` +在协程中,我们所有的方法都是直接返回值的,并没有返回错误,我们在执行过程中的错误是通过 `co_getError()` 获取的,比如我们有以下从网络获取数据的接口,在失败的时候, promise 会 `reject:error`

+ +```objc - (CCOPromise*)co_GET:(NSString*)url parameters:(NSDictionary*)parameters{ CCOPromise *promise = [CCOPromise promise]; @@ -95,7 +101,7 @@ co_launch创建的协程默认在当前线程进行调度 那我们在协程中可以如下使用: -``` +```objc co_launch(^{ id response = await([self co_GET:feedModel.feedUrl parameters:nil]); if(co_getError()){ @@ -105,14 +111,13 @@ co_launch(^{ }); ``` +#### 生成器 -#### Generator +* 创建生成器 -* create generator +我们使用 `co_sequence` 创建生成器 -我们使用co_sequence创建Generator - -``` +```objc COCoroutine *co1 = co_sequence(^{ int index = 0; while(co_isActive()){ @@ -122,9 +127,9 @@ COCoroutine *co1 = co_sequence(^{ }); ``` -在其他协程中,我们可以调用next方法,获取生成器中的数据 +在其他协程中,我们可以调用 `next` 方法,获取生成器中的数据 -``` +```objc co_launch(^{ for(int i = 0; i < 10; i++){ val = [[co1 next] intValue]; @@ -136,7 +141,7 @@ co_launch(^{ 生成器可以在很多场景中进行使用,比如消息队列、批量下载文件、批量加载缓存等: -``` +```objc int unreadMessageCount = 10; NSString *userId = @"xxx"; COSequence *messageSequence = sequenceOnBackgroundQueue(@"message_queue", ^{ @@ -156,30 +161,30 @@ co(^{ }); ``` -通过生成器,我们可以把传统的生产者加载数据-》通知消费者模式,变成消费者需要数据-》告诉生产者加载模式,避免了在多线程计算中,需要使用很多共享变量进行状态同步,消除了在某些场景下对于锁的使用

- +通过生成器,我们可以把传统的生产者加载数据->通知消费者模式,变成消费者需要数据->告诉生产者加载模式,避免了在多线程计算中,需要使用很多共享变量进行状态同步,消除了在某些场景下对于锁的使用

#### Actor -> **_Actor的概念来自于Erlang,在AKKA中,可以认为一个Actor就是一个容器,用以存储状态、行为、Mailbox以及子Actor与Supervisor策略。Actor之间并不直接通信,而是通过Mail来互通有无。_** -* create actor +> **_ Actor 的概念来自于 Erlang ,在 AKKA 中,可以认为一个 Actor 就是一个容器,用以存储状态、行为、Mailbox 以及子 Actor 与 Supervisor 策略。Actor 之间并不直接通信,而是通过 Mail 来互通有无。_** -我们可以使用co_actor_onqueue在指定线程创建actor +* 创建 actor -``` +我们可以使用 `co_actor_onqueue` 在指定线程创建 actor + +```objc CCOActor *actor = co_actor_onqueue(^(CCOActorChan *channel) { - ... //定义actor的状态变量 + ... //定义 actor 的状态变量 for(CCOActorMessage *message in channel){ ...//处理消息 } }, q); ``` -* 给actor发送消息 +* 给 actor 发送消息 -actor的send方法可以给actor发送消息 +actor 的 `send` 方法可以给 actor 发送消息 -``` +```objc CCOActor *actor = co_actor_onqueue(^(CCOActorChan *channel) { ... //定义actor的状态变量 for(CCOActorMessage *message in channel){ @@ -193,9 +198,11 @@ CCOActor *actor = co_actor_onqueue(^(CCOActorChan *channel) { ``` -#### tuple -* create tuple -使用co_tuple方法来创建元组 +#### 元组 + +* 创建元组 + +使用 `co_tuple` 方法来创建元组 ```objc COTuple *tup = co_tuple(nil, @10, @"abc"); @@ -203,10 +210,12 @@ NSAssert(tup[0] == nil, @"tup[0] is wrong"); NSAssert([tup[1] intValue] == 10, @"tup[1] is wrong"); NSAssert([tup[2] isEqualToString:@"abc"], @"tup[2] is wrong"); ``` -可以在tuple中存储任何数据 -* unpack tuple -可以使用co_unpack方法从tuple中取值 +可以在元组中存储任何数据 + +* 元组取值 + +可以使用 `co_unpack` 方法从元组中取值 ```objc id val0; @@ -233,8 +242,9 @@ co_unpack(nil, nil, &str1) = co_tuple(nil, @10, @"abc"); NSAssert([str1 isEqualToString:@"abc"], @"str1 is wrong"); ``` -* use tuple in coroutine -first create a promise that resolve tuple value +* 在协程中使用元组 + +首先创建一个 promise 来处理元组里的值 ```objc COPromise* @@ -252,9 +262,9 @@ cotest_loadContentFromFile(NSString *filePath){ } ``` -then you can fetch the value like this: +然后,你可以像下面这样获取元组里的值: -``` +```objc co_launch(^{ NSString *tmpFilePath = nil; NSData *data = nil; @@ -265,12 +275,14 @@ co_launch(^{ XCTAssert(error == nil, @"error is wrong"); }); ``` -use tuple you can get multiple values from await return -#### Actual case using coobjc -我们以GCDFetchFeed开源项目中Feeds流更新的代码为例,演示一下协程的实际使用场景和优势,下面是原始的不使用协程的实现: +使用元组你可以从 `await` 返回值中获取多个值 -``` +#### 演示项目 + +我们以 GCDFetchFeed 开源项目中 Feeds 流更新的代码为例,演示一下协程的实际使用场景和优势,下面是原始的不使用协程的实现: + +```objc - (RACSignal *)fetchAllFeedWithModelArray:(NSMutableArray *)modelArray { @weakify(self); return [RACSignal createSignal:^RACDisposable *(id subscriber) { @@ -304,9 +316,9 @@ use tuple you can get multiple values from await return //通知单个完成 dispatch_group_leave(group); }]; - + });//end dispatch async - + } failure:^(NSURLSessionTask *operation, NSError *error) { NSLog(@"Error: %@", error); dispatch_async(fetchFeedQueue, ^{ @@ -316,12 +328,12 @@ use tuple you can get multiple values from await return model.fid = [x integerValue]; dispatch_group_leave(group); }]; - + });//end dispatch async - + }]; - - }//end for + + } //end for //全完成后执行事件 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ [subscriber sendCompleted]; @@ -331,9 +343,9 @@ use tuple you can get multiple values from await return } ``` -下面是viewDidLoad中对上述方法的调用: +下面是 `viewDidLoad` 中对上述方法的调用: -``` +```objc [UIApplication sharedApplication].networkActivityIndicatorVisible = YES; self.fetchingCount = 0; //统计抓取数量 @weakify(self); @@ -376,7 +388,7 @@ use tuple you can get multiple values from await return 上述代码无论从可读性还是简洁性上都比较差,下面我们看一下使用协程改造以后的代码: -``` +```objc - (SMFeedModel*)co_fetchFeedModelWithUrl:(SMFeedModel*)feedModel{ feedModel.isSync = NO; id response = await([self co_GET:feedModel.feedUrl parameters:nil]); @@ -396,9 +408,9 @@ use tuple you can get multiple values from await return } ``` -下面是viewDidLoad中使用协程调用该接口的地方: +下面是 `viewDidLoad` 中使用协程调用该接口的地方: -``` +```objc co_launch(^{ for (NSUInteger index = 0; index < self.feeds.count; index++) { SMFeedModel *model = self.feeds[index]; @@ -427,18 +439,20 @@ co_launch(^{ 协程化改造之后的代码,变得更加简单易懂,不易出错 -#### Swift -coobjc fully supports Swift through top-level encapsulation, enabling us to enjoy the coroutine ahead of time in Swift. -Because Swift has richer and more advanced syntax support, coobjc is more elegant in Swift, for example: +#### Swift -``` +coobjc 通过上层封装来全面支持 Swift ,这使得我们可以提早在 Swift 中使用协程。 + +由于 Swift 拥有更丰富和更高阶的语法支持,因而 coobjc 在 Swift 中的使用会更优雅,例如: + +```swift func test() { co_launch {//create coroutine //fetch data asynchronous let resultStr = try await(channel: co_fetchSomething()) print("result: \(resultStr)") } - + co_launch {//create coroutine //fetch data asynchronous let result = try await(promise: co_fetchSomethingAsynchronous()) @@ -454,47 +468,50 @@ func test() { ``` ## 0x4 协程的优势 + * 简明 * 概念少:只有很少的几个操作符,相比响应式几十个操作符,简直不能再简单了 * 原理简单: 协程的实现原理很简单,整个协程库只有几千行代码 * 易用 - * 使用简单:它的使用方式比GCD还要简单,接口很少 + * 使用简单:它的使用方式比 GCD 还要简单,接口很少 * 改造方便:现有代码只需要进行很少的改动就可以协程化,同时我们针对系统库提供了大量协程化接口 * 清晰 * 同步写异步逻辑:同步顺序方式写代码是人类最容易接受的方式,这可以极大的减少出错的概率 - * 可读性高: 使用协程方式编写的代码比block嵌套写出来的代码可读性要高很多 + * 可读性高: 使用协程方式编写的代码比 block 嵌套写出来的代码可读性要高很多 * 性能 * 调度性能更快:协程本身不需要进行内核级线程的切换,调度性能快,即使创建上万个协程也毫无压力 - * 减少卡顿卡死: 协程的使用以帮助开发减少锁、信号量的滥用,通过封装会引起阻塞的IO等协程接口,可以从根源上减少卡顿、卡死,提升应用整体的性能 + * 减少卡顿卡死: 协程的使用以帮助开发减少锁、信号量的滥用,通过封装会引起阻塞的 IO 等协程接口,可以从根源上减少卡顿、卡死,提升应用整体的性能 ## 0x5 交流 -* If you **need help**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/coobjc). (Tag 'coobjc') -* If you'd like to **ask a general question**, use [Stack Overflow](http://stackoverflow.com/questions/tagged/coobjc). -* If you **found a bug**, _and can provide steps to reliably reproduce it_, open an issue. -* If you **have a feature request**, open an issue. -* If you **want to contribute**, submit a pull request. + +* 如果你**需要帮助**,请使用 [Stack Overflow](http://stackoverflow.com/questions/tagged/coobjc)。(标签为 'coobjc') +* 如果你想**提问**,请使用 [Stack Overflow](http://stackoverflow.com/questions/tagged/coobjc)。 +* 如果你**发现了 bug**,并且可以提供可稳定复现的步骤,请开 issue。 +* 如果你有**新特性需求**,请开 issue。 +* 如果你**想贡献代码**,请提交 PR。 ## 0x6 单元测试 + coobjc includes a suite of unit tests within the Tests subdirectory. These tests can be run simply be executed the test action on the platform framework you would like to test. -## 0x7 Credits -coobjc couldn't exist without: +## 0x7 致谢 -* [Promises](https://github.com/google/promises) - Google's Objective-C and Swift Promises framework. -* [libtask](https://swtch.com/libtask/) - A simple coroutine library. -* [movies](https://github.com/KMindeguia/movies) - a ios demo app, we use the code in coobjc examples -* [v2ex](https://github.com/singro/v2ex) - An iOS client for v2ex.com, we use the code in coobjc examples -* [tuples](https://github.com/atg/tuples) - Objective-C tuples. +coobjc 离不开下面这些项目的帮助: + +* [Promises](https://github.com/google/promises) - Google 开发的 Objective-C 和 Swift 版本的 Promise 框架 +* [libtask](https://swtch.com/libtask/) - 一个简易的协程库 +* [movies](https://github.com/KMindeguia/movies) - 一个 iOS 演示项目,我们在 coobjc 的示例中使用了其中的代码 +* [v2ex](https://github.com/singro/v2ex) - v2ex.com 的 iOS 客户端项目,我们在 coobjc 的示例中使用了其中的代码 +* [tuples](https://github.com/atg/tuples) - Objective-C 元组 ## 0x8 作者 + * [pengyutang125](https://github.com/pengyutang125) * [NianJi](https://github.com/NianJi) * [intheway](https://github.com/intheway) * [ValiantCat](https://github.com/ValiantCat) * [jmpews](https://github.com/jmpews) -## 0x9 License -coobjc is released under the Apache 2.0 license. See [LICENSE](LICENSE) for details. -
- +## 0x9 协议 +coobjc 使用 Apache 2.0 协议,详情见 [LICENSE](LICENSE) 文件。