zoukankan      html  css  js  c++  java
  • 多线程-NSOperation中使用ASIHttpRequest注意事项

    最近做的iPhone项目中有一如下功能:

    app在用户许可后将本地Photos的照片上传到服务器,期间用户可以做其他任何操作,等上传成功后弹出一个toast通知用户。

    原先的代码结构是:

    1. 获取照片的操作放在NSOperation的子类A中
    2. 获取完照片后,逐个生成一个上传类B(此上传类是ASIFormDataRequest的子类),并把它添加到NSOperationQueue中。

    其中operationqueue设置了最大运行数是1,但是实际测试下来发现所有的上传都是并发的,一查代码,发现上传类B居然没有实现main,就一个init函数。初始化完之后直接startAsynchronous了,然后返回self。真是奇葩~~

    于是将上传类B修改,添加了main函数,但是运行的时候出错:

    - (void)reportFinished
    {
        if (delegate && [delegate respondsToSelector:didFinishSelector]) {
            [delegate performSelector:didFinishSelector withObject:self];
        }---------------------->提示bad_access的错误
    
        #if NS_BLOCKS_AVAILABLE
        if(completionBlock){
            completionBlock();
        }
        #endif
    
        if (queue && [queue respondsToSelector:@selector(requestFinished:)]) {
            [queue performSelector:@selector(requestFinished:) withObject:self];
        }
    }

    查看delegate的值,发现已经overrelease了。B在设置的时候,将delegate设置为A的实例了,A的实例怎么会不等B的返回就结束了呢?

    原来A本身是一个operation,假设运行在次线程 M中。B因为是继承ASIFormDataRequest,其实也是一个NSOperation,也就是说B运行的时候也是运行在次线程N中的。因为B使用的是异步运行,N必然不同于M。而A在将上传操作结束完以后,就结束了,系统就会回收A的内存。这个时候在N中运行的B尚未收到响应。等到response返回的时候,A早就已经释放了,所以就会有如上的错误。

    怎么解决呢?有同事是把A设置为property。这样可以解决,但是当需要调用A的类很多的时候,就会比较麻烦。

    其实解决的办法很简单,就是在A中重载isFinished方法,当确定所有的照片上传上去后返回YES否则返回NO,这样我们就可以控制A,避免系统“过早”的释放。

    与此同时我们发现,ASIHttpRequest的delegate响应都会路由到主线程:

    - (void)requestFinished
    {
    #if DEBUG_REQUEST_STATUS || DEBUG_THROTTLING
        NSLog(@"[STATUS] Request finished: %@",self);
    #endif
        if ([self error] || [self mainRequest]) {
            return;
        }
        if ([self isPACFileRequest]) {
            [self reportFinished];
        } else {
            [self performSelectorOnMainThread:@selector(reportFinished) withObject:nil waitUntilDone:[NSThread isMainThread]];
        }
    }

    stackoverflow上有关于这样做的讨论,这里要说明的是因为B是运行在后台,delegate是A,不需要在主线程响应。我们可以在B中重载上述函数,将performSelectorOnMainThread:函数去掉,直接调用reportFinished。

    进一步考虑,iOS上获取本地照片现在一般用ALAssetsLibrary,这个库一般是用block去枚举,换言之获得照片内容的操作已经是在次线程中操作的了。

    这样一来A也就可以不需要是NSOperation,是个一般的NSObject即可。

    最近项目添加了很多“奇葩”的功能,可是参与的决定权不在自己这边,虽然我列出了很多不应该这样做的理由和依据。但是需求人员都以本国的用户需求为借口——看来公司越来越成为外资公司在华的外包公司了。这个职位也变得越来越乏味,虽然不见得能马上跳槽,但是也学会了在“逆境”中坚强:学习产品的设计,和非开发人员的沟通,重构代码。觉得有句话说的真好:要想做自己想做的事,就得先做自己不想做的事。

    与诸君共勉!

  • 相关阅读:
    CXF 与Spring整合配置
    CloudStack采用spring加载bean(cloud-framework-spring-module模块)
    CloudStack服务引擎配置(cloud-engine-service模块)
    Oracle用户、权限、角色管理
    合并日常使用指南
    oracle表空间建立与用户创建删除
    oracle 创建同义词
    Eclipse下svn的创建分支/合并/切换使用
    字符串转数字 简单代码实现
    Linux下调试段错误 (gdb,core,ulimit)
  • 原文地址:https://www.cnblogs.com/scorpiozj/p/3149066.html
Copyright © 2011-2022 走看看