<output id="ilehw"><bdo id="ilehw"><nobr id="ilehw"></nobr></bdo></output>
        <dl id="ilehw"><font id="ilehw"></font></dl>
          1. iOS 协程开发框架 coobjc

            iOS 协程开发框架 coobjc

            Apache-2.0
            跨平台
            阿里巴巴
            2019-02-28
            h4cd

            coobjc 为 Objective-C 和 Swift 提供了协程功能。coobjc 支持 await、generator 和 actor model,接口参考了 C# 、Javascript 和 Kotlin 中的很多设计。我们还提供了 cokit 库为 Foundation 和 UIKit 中的部分 API 提供了协程化支持,包括 NSFileManager、JSON、NSData 与 UIImage 等。coobjc 也提供了元组的支持。

            0x0 iOS 异?#22870;?#31243;问题

            基于 Block 的异?#22870;?#31243;回调是目前 iOS 使用最广泛的异?#22870;?#31243;方式,iOS 系?#31243;?#20379;的 GCD 库让异步开发变得很简单?#22870;悖?#20294;是基于这种编程方式的缺点也有很多,主要有以下几点:

            • 容易进入"嵌套地狱"
            • 错误处理复杂和冗长
            • 容易忘记调用 completion handler
            • 条件执行变得很困难
            • 从互相独立的调用中组合返回结果变得极其困难
            • 在错误的线程中继续执行
            • 难以定位原因的多线程崩溃
            • 锁和信号量滥?#20040;?#26469;的卡顿、卡死

            上述问题?#20174;?#21040;线上应用本身就会出现大量的多线程崩溃。

            0x1 解决方案

            上述问题在很多系统和语言中都会遇到,解决问题的标准方式就是使用协程。这里不介绍太多的理论,简单说协程就是对基础函数的扩展,可以让函数异步执行的时候挂起然后返回值。协程可以用来实现 generator ,异步模型以及其他强大的能力。

            Kotlin 是这两年由 JetBrains 推出的支持现代多平台应用的静态编程语言,支持 JVM ,Javascript ,目前?#37096;?#20197;在 iOS 上执行,这两年在开发者社区中也是比较火。

            在 Kotlin 语言中基于协程的 async/await ,generator/yield 等异步化技术?#23478;?#32463;成了语法标配,Kotlin 协程相关的介绍,大家可以参考:协程框架设计 文档。

          2. 阅读 coobjc Objective-C Guide 文档。
          3. 阅读 coobjc Swift Guide 文档。
          4. 阅读 cokit framework 文档, 学习如?#38382;?#29992;系统接口封装的 api 。
          5. 0x33 特性

            async/await

            • 创建协程

            使用 co_launch 方法创建协程

            co_launch(^{
                ...
            });

            co_launch 创建的协程默认在当前线程进行调度

            • await 异步方法

            在协程中我们使用 await 方法等待异步方法执行结束,得到异步执行结果

            - (void)viewDidLoad{
                ...
            		co_launch(^{
                		NSData *data = await(downloadDataFromUrl(url));
                		UIImage *image = await(imageFromData(data));
                		self.imageView.image = image;
            		});
            }

            上述代码将原本需要 dispatch_async 两次的代码变成了顺序执行,代码更加简洁

            • 错误处理

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

            - (CCOPromise*)co_GET:(NSString*)url
              parameters:(NSDictionary*)parameters{
                CCOPromise *promise = [CCOPromise promise];
                [self GET:url parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
                    [promise fulfill:responseObject];
                } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
                    [promise reject:error];
                }];
                return promise;
            }

            那我们在协程中可以如下使用:

            co_launch(^{
                id response = await([self co_GET:feedModel.feedUrl parameters:nil]);
                if(co_getError()){
                    //处理错误信息
                }
                ...
            });

            生成器

            • 创建生成器

            我们使用 co_sequence 创建生成器

            COCoroutine *co1 = co_sequence(^{
                        int index = 0;
                        while(co_isActive()){
                            yield_val(@(index));
                            index++;
                        }
                    });

            在其他协程中,我们可以调用 next 方法,获取生成器中的数据

            co_launch(^{
                        for(int i = 0; i < 10; i++){
                            val = [[co1 next] intValue];
                        }
                    });
            • 使用场景

            生成器可以在很多场景中进行使用,比如消息队列、批量下载文件、批?#32771;?#36733;缓存等:

            int unreadMessageCount = 10;
            NSString *userId = @"xxx";
            COSequence *messageSequence = sequenceOnBackgroundQueue(@"message_queue", ^{
               //在后台线程执行
                while(1){
                    yield(queryOneNewMessageForUserWithId(userId));
                }
            });
            
            //主线程更新UI
            co(^{
               for(int i = 0; i < unreadMessageCount; i++){
                   if(!isQuitCurrentView()){
                       displayMessage([messageSequence take]);
                   }
               }
            });

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

            Actor

            _ Actor 的概念来自于 Erlang ,在 AKKA 中,可以认为一个 Actor 就是一个容器,用?#28304;?#20648;状态、行为、Mailbox 以及子 Actor 与 Supervisor 策略。Actor 之间并不直接通信,而是通过 Mail 来互通有无。_

            • 创建 actor

            我们可以使用 co_actor_onqueue 在指定线程创建 actor

            CCOActor *actor = co_actor_onqueue(^(CCOActorChan *channel) {
                ...  //定义 actor 的状态变量
                for(CCOActorMessage *message in channel){
                    ...//处理消息
                }
            }, q);
            • 给 actor 发送消息

            actor 的 send 方法可以给 actor 发送消息

            CCOActor *actor = co_actor_onqueue(^(CCOActorChan *channel) {
                ...  //定义actor的状态变量
                for(CCOActorMessage *message in channel){
                    ...//处理消息
                }
            }, q);
            
            // 给actor发送消息
            [actor send:@"sadf"];
            [actor send:@(1)];
            

            元组

            • 创建元组

            使用 co_tuple 方法?#21019;?#24314;元组

            COTuple *tup = co_tuple(nil, @10, @"abc");
            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");

            可以在元组中存储任?#38382;?#25454;

            • 元组取值

            可以使用 co_unpack 方法从元组中取值

            id val0;
            NSNumber *number = nil;
            NSString *str = nil;
            co_unpack(&val0, &number, &str) = co_tuple(nil, @10, @"abc");
            NSAssert(val0 == nil, @"val0 is wrong");
            NSAssert([number intValue] == 10, @"number is wrong");
            NSAssert([str isEqualToString:@"abc"], @"str is wrong");
            
            co_unpack(&val0, &number, &str) = co_tuple(nil, @10, @"abc", @10, @"abc");
            NSAssert(val0 == nil, @"val0 is wrong");
            NSAssert([number intValue] == 10, @"number is wrong");
            NSAssert([str isEqualToString:@"abc"], @"str is wrong");
            
            co_unpack(&val0, &number, &str, &number, &str) = co_tuple(nil, @10, @"abc");
            NSAssert(val0 == nil, @"val0 is wrong");
            NSAssert([number intValue] == 10, @"number is wrong");
            NSAssert([str isEqualToString:@"abc"], @"str is wrong");
            
            NSString *str1;
            
            co_unpack(nil, nil, &str1) = co_tuple(nil, @10, @"abc");
            NSAssert([str1 isEqualToString:@"abc"], @"str1 is wrong");
            • 在协程中使用元组

            首先创建一个 promise ?#21019;?#29702;元组里的值

            COPromise<COTuple*>*
            cotest_loadContentFromFile(NSString *filePath){
                return [COPromise promise:^(COPromiseFullfill  _Nonnull resolve, COPromiseReject  _Nonnull reject) {
                    if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
                        NSData *data = [[NSData alloc] initWithContentsOfFile:filePath];
                        resolve(co_tuple(filePath, data, nil));
                    }
                    else{
                        NSError *error = [NSError errorWithDomain:@"fileNotFound" code:-1 userInfo:nil];
                        resolve(co_tuple(filePath, nil, error));
                    }
                }];
            }

            然后,你可以像下面这样获取元组里的值:

            co_launch(^{
                NSString *tmpFilePath = nil;
                NSData *data = nil;
                NSError *error = nil;
                co_unpack(&tmpFilePath, &data, &error) = await(cotest_loadContentFromFile(filePath));
                XCTAssert([tmpFilePath isEqualToString:filePath], @"file path is wrong");
                XCTAssert(data.length > 0, @"data is wrong");
                XCTAssert(error == nil, @"error is wrong");
            });

            使用元组你可?#28304;?nbsp;await 返回值中获取多个值。

            的码云指数为
            超过 的项目
            加载中

            评论(1)

            开源中国首席罗纳尔多
            您好,java有协程吗?x协程和线程的区别是什么?

            暂无资讯

            暂无问答

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

            阿里妹导读:刚刚,阿里巴巴正式对外开源了基于 Apache 2.0 协议的协程开发框架 coobjc,开发者们可以在 Github 上自主下载。 coobjc是为iOS平台打造的开源协程开发框架,支持Objective-C和S...

            03/01 13:09
            43
            0
            D2 日报 2019年 03月 08日

            新闻 ?? Introducing draft pull requests 非中文 GitHub 新增 Draft Pull Request 协作流程 github.blog 开源项目 ?? rocky/python-uncompyle6 非中文 watch 39 star 1022 fork 127 Py...

            03/08 09:04
            5
            0

            没有更多内容

            加载失败,请刷新页面

            没有更多内容

            返回顶部
            顶部
            广东快乐十分实时开奖

                  <output id="ilehw"><bdo id="ilehw"><nobr id="ilehw"></nobr></bdo></output>
                  <dl id="ilehw"><font id="ilehw"></font></dl>

                            <output id="ilehw"><bdo id="ilehw"><nobr id="ilehw"></nobr></bdo></output>
                            <dl id="ilehw"><font id="ilehw"></font></dl>
                              1. 湖北11选5复试怎么算 新疆喜乐彩投注 快玩三张牌电脑版 欧洲奥地利秒速时时彩 体育彩票走势图带走线 qq德州扑克 天津11选5论坛 赛马资信香港赛马会 陕西快乐十分管官网 快乐赛车官方网站 谁有极速时时彩计划 中国福利彩票app下载 三肖中特牛虎鼠 湖北十一选五开奖结果十 彩之星福彩3d