执行相互调用的方法。全部在后台

时间:2013-06-25 03:09:00

标签: ios concurrency nsoperation nsoperationqueue

在我的应用程序中,我几乎没有相互调用来解析XML下载的feed的方法,我需要在后台使用NSOperationNSOperationQueue进行所有解析,因为现在它正在执行主线程和冻结整个应用程序。

我的应用程序逻辑类似于以下内容:

-(IBAction) callSync{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:APIURL]];

    AFURLConnectionOperation *operation = [[AFURLConnectionOperation alloc] initWithRequest:request];
    operation.completionBlock = ^{
          //1
          [self startParsing:operation.responseString];
    };
    [operation start];
}

//2
-(void)startParsing:(NSString*)str
{
         //some logic
         [self traverseXML:str];//Call traverseXML
}
//3
- (void) traverseXML:(TBXMLElement *)element {
        //Some logic
        [self saveFile:localWS];//CallsaveFile
}
//4
-(void) saveFile : (WorkFile *)_workFile{
        //Some logic
}

我的问题是:我是否应该为每个方法创建一个NSOperation类,我的意思是startParsing一个,traverseXML一个,等等?或者只是它足以创建一个NSOperation子类并在其中执行所有实现方法。

4 个答案:

答案 0 :(得分:1)

有几点想法:

  1. 您不需要执行任何NSOperation子类化。你可以,如果你真的想,但在这种情况下似乎完全没必要。更简单的是,例如,如果您有一个单独的操作队列用于解析过程,您可以只执行addOperationWithBlock或创建NSBlockOperation并将其添加到解析队列中。例如:

    [self.parseQueue addOperationWithBlock:^{
        [self startParsing:operation.responseString]; // this will call traverseXML and saveFile, so if those are all synchronous, then all three are within this one block
    }];
    

    就个人而言,我唯一一次经历子类化NSOperation的额外工作是我必须实现自己的自定义取消逻辑,或者我正在创建一个包含某些任务的操作,这本身就是异步,我想控制操作何时设置isFinished。或者,当操作本身达到一定程度的复杂性时,我将它NSOperation子类化,将其抽象为单独的操作类可以提高代码的易读性。但到目前为止,您所描述的内容并未表明您需要进行NSOperation子类化。只使用NSBlockOperation或者更好,仅使用addOperationWithBlock,更简单的是将NSOperation子类化。

  2. 因此,抛开“子类化NSOperation”问题,让我们转向您是否需要对三种方法进行单独操作。鉴于您按顺序执行这三个任务,那么这最初听起来像是单个操作的候选者。如果您愿意,您当然可以创建三个单独的操作,但我没有看到任何令人信服的商业案例,因为这种复杂程度更高。

  3. 在我看来,更有趣的问题是“我将创建什么操作队列”。为此,这是一个问题,你是否想要并发(例如,在网络操作中非常有用)以及在多大程度上(例如,最好不要发出太多并发网络请求,即最多坚持四个或五个)。如果您正在下载多个XML文件并对其进行解析,那么这只是一个问题。在那种情况下,我可以想象您可能有一个队列用于网络操作,另一个队列用于解析操作。这样,您可以将网络队列配置为享受一些并发,但约束maxConcurrentOperationCount,以便您没有太多的并发网络请求。解析/保存操作可能具有不同的并发功能(例如,如果您没有实例化单独的解析器对象,则您的解析可能根本不支持并发)。通常,它归结为平衡并发性能增益与内存消耗和此类并发所需的程序复杂性。

答案 1 :(得分:0)

我会选择一个NSOperation,因为您想要做的3个操作实际上并不是自己生活。

也许只有保存应该在另一个类中,但我不认为在同一个操作中执行它是个问题。

答案 2 :(得分:0)

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    //parse
    //traverse
    //save

    dispatch_async (dispatch_get_main_queue (), ^{

        //update UI
    });
});

答案 3 :(得分:0)

顺便说一下,我不太清楚为什么你的代码会阻塞主线程。例如,当我在完成块中放置一个断点时,我可以清楚地看到它发生在后台线程上(本例中为#5):

completion

因此,我应该能够在不影响用户体验的情况下执行我想要的任何耗时过程(例如我的命令睡眠五秒钟,这在我的示例项目中不会冻结UI)。

作为对比点,如果我在进度块中放置一个断点(AFNetworking调度回主队列),它就在主线程上,正如预期的那样:

progress

由于这是在主队列上发生的,所以我必须非常小心,确保我不会在那里花费太多时间,因为这会阻止用户界面。

(顺便说一句,你可能需要 control - 点击上面的图片,在浏览器的另一个标签/窗口中打开它们,以便清楚地看到它们。)

但是,直言不讳地说,主要的解析过程,如果你在完成块中这样做,应该已经异步运行了。当然,我可以对它进行优化,将AFURLConnectionOperation提交给某个网络队列,并将解析操作添加到一个单独的解析操作队列中,但是这个代码应该已经异步运行了。

我只提到这一点,因为它让我觉得您即将开始将此解析转换为后台操作的过程,但似乎值得确认您的代码首先冻结您的应用程序的原因。

例如,如果解析过程使用某种锁定机制(例如@synchronized)来同步解析(当您异步执行某些操作时,您必须仔细思考如何进行解析synchronize it properly以确保线程安全),只需将此代码移动到另一个后台操作就无法解决这种情况。同样,如果您的解析器将某些代码分派给dispatch_get_main_queue(),您也必须重构该代码。