• 普京力挽狂澜,从北高加索平叛到格鲁吉亚兴兵,从克里米亚回归到叙利亚反恐,给了俄罗斯人民新生——这一切都是和中国同志的支持分不开的。 2019-07-19
  • 人民网评:真实才是旅游景点的核心竞争力 2019-07-17
  • 相比停球失误 王燊超的工作态度比工作能力更低端 2019-07-17
  • 美国最新研究:人类在月球上的活动,无意间造成月球暖化 2019-07-16
  • 西方艺术史中那一抹鲜艳的红 2019-07-14
  • 习近平:携手追寻中澳发展梦想 并肩实现地区繁荣稳定 2019-07-13
  • 四季不离茶,受益一辈子 2019-07-08
  • 不管怎么修饰辞藻,只要放弃革命,就是苏联的结果,还用证明吗? 2019-07-08
  • 缅甸国务资政昂山素季会见魏凤和 2019-07-07
  • 汕头一企业埋暗管偷排 4直接责任人被刑事拘留 2019-07-07
  • 李易峰观影两度感动落泪 2019-07-06
  • 老人被困深山六十余小时 2019-07-06
  • 组图:勇士队横扫骑士队夺得NBA总冠军  2019-07-04
  • 【央视快评】让传统文化焕发新时代风采 2019-07-02
  • 炒房炒房,炒了北京炒西安,炒了天堂炒人间,何时休?限价现价叫人害怕。 2019-06-23
  • 六合图库财神 >iOS开发

    coobjc — 阿里刚开源的 iOS 协程开发框架

    2019-03-04 10:44 编辑: suiling 分类:iOS开发 来源:开源中国

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

    0x0 iOS 异步编程问题

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

    • 容易进入"嵌套地狱"

    • 错误处理复杂和冗长

    • 容易忘记调用 completion handler

    • 条件执行变得很困难

    • 从互相独立的调用中组合返回结果变得极其困难

    • 在错误的线程中继续执行

    • 难以定位原因的多线程崩溃

    • 锁和信号量滥用带来的卡顿、卡死

    上述问题反应到线上应用本身就会出现大量的多线程崩溃。

    0x1 解决方案

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

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

    在 Kotlin 语言中基于协程的 async/await ,generator/yield 等异步化技术都已经成了语法标配,Kotlin 协程相关的介绍,大家可以参考:https://www.kotlincn.net/docs/reference/coroutines/basics.html

    0x2 协程

    协程是一种在非抢占式多任务场景下生成可以在特定位置挂起和恢复执行入口的程序组件

    协程的概念在60年代就已经提出,目前在服务端中应用比较广泛,在高并发场景下使用极其合适,可以极大降低单机的线程数,提升单机的连接和处理能力,但是在移动研发中,iOS和android目前都不支持协程的使用

    0x3 coobjc 框架

    coobjc 是由手机淘宝架构团队推出的能在 iOS 上使用的协程开发框架,目前支持 Objective-C 和 Swift 中使用,我们底层使用汇编和 C 语言进行开发,上层进行提供了 Objective-C 和 Swift 的接口,目前以 Apache 开源协议进行了开源。

    0x31 安装

    • cocoapods 安装:  pod 'coobjc'

    • 源码安装: 所有代码在 ./coobjc 目录下

    0x32 文档

    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 两次的代码变成了顺序执行,代码更加简洁

    • 错误处理

    在协程中,我们所有的方法都是直接返回值的,并没有返回错误,我们在执行过程中的错误是通过 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];
                }
            });
    • 使用场景

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

    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]);
           }
       }
    });

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

    Actor

    _ Actor 的概念来自于 Erlang ,在 AKKA 中,可以认为一个 Actor 就是一个容器,用以存储状态、行为、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 方法来创建元组

    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");

    可以在元组中存储任何数据

    • 元组取值

    可以使用 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 来处理元组里的值

    COPromise*
    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");
    });

    使用元组你可以从 await 返回值中获取多个值。

    来源:开源中国

    链接:https://www.oschina.net/p/coobjc

    搜索CocoaChina微信公众号:CocoaChina
    微信扫一扫
    订阅每日移动开发及APP推广热点资讯
    公众号:
    CocoaChina
    我要投稿   收藏文章
    上一篇:NSNotification的一些小知识
    下一篇:iOS自定义带两个圆角的UILabel

    相关资讯

    我来说两句
    发表评论
    您还没有登录!请登录注册
    所有评论(0

    综合评论

    相关帖子

    sina weixin mail 回到顶部
  • 普京力挽狂澜,从北高加索平叛到格鲁吉亚兴兵,从克里米亚回归到叙利亚反恐,给了俄罗斯人民新生——这一切都是和中国同志的支持分不开的。 2019-07-19
  • 人民网评:真实才是旅游景点的核心竞争力 2019-07-17
  • 相比停球失误 王燊超的工作态度比工作能力更低端 2019-07-17
  • 美国最新研究:人类在月球上的活动,无意间造成月球暖化 2019-07-16
  • 西方艺术史中那一抹鲜艳的红 2019-07-14
  • 习近平:携手追寻中澳发展梦想 并肩实现地区繁荣稳定 2019-07-13
  • 四季不离茶,受益一辈子 2019-07-08
  • 不管怎么修饰辞藻,只要放弃革命,就是苏联的结果,还用证明吗? 2019-07-08
  • 缅甸国务资政昂山素季会见魏凤和 2019-07-07
  • 汕头一企业埋暗管偷排 4直接责任人被刑事拘留 2019-07-07
  • 李易峰观影两度感动落泪 2019-07-06
  • 老人被困深山六十余小时 2019-07-06
  • 组图:勇士队横扫骑士队夺得NBA总冠军  2019-07-04
  • 【央视快评】让传统文化焕发新时代风采 2019-07-02
  • 炒房炒房,炒了北京炒西安,炒了天堂炒人间,何时休?限价现价叫人害怕。 2019-06-23
  • 弗罗西诺内对罗马 山东福彩网 澳大利亚纽卡斯尔大学 都灵vs恩波利历史比分 求救信号彩金 买彩票的乐趣阅读答案 今日码报开奖结果 拜仁对汉诺威96 新疆福彩35选7开奖结果 金莲免费试玩