1

在我的应用程序中,我很少有方法相互调用来解析 XML 下载的提要,我需要在后台进行所有解析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为每个方法子类化一个类,我的意思是一个 for startParsing,一个 fortraverseXML等等?或者仅仅创建一个NSOperation子类并在其中完成所有实现方法就足够了。

4

4 回答 4

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子类化。并且仅仅使用NSBlockOperationor,更好的addOperationWithBlock是, just 比子类化要简单得多NSOperation

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

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

于 2013-06-25T05:45:06.670 回答
0
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    //parse
    //traverse
    //save

    dispatch_async (dispatch_get_main_queue (), ^{

        //update UI
    });
});
于 2013-06-25T03:19:51.563 回答
0

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

完成

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

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

进步

由于这发生在主队列上,因此我必须非常小心,以确保我不会在那里做任何耗时的事情,因为这会阻塞 UI。

(顺便说一句,您可能需要control单击上面的图像才能在浏览器的另一个选项卡/窗口中打开它们才能清晰地看到它们。)

但是,最重要的是,如果您在完成块中执行此操作,主解析过程应该已经异步运行。当然,我可能会改进它以将其提交AFURLConnectionOperation到某个网络队列并将解析操作添加到单独的解析操作队列中,但我觉得这段代码应该已经异步运行了。

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

例如,如果解析过程正在使用某种锁定机制,例如@synchronized,来同步解析(并且当您异步执行某些操作时,您必须仔细考虑如何正确同步它以确保线程安全) ,简单地将此代码移动到另一个后台操作并不能解决这种情况。同样,如果您的解析器将一些代码分派到dispatch_get_main_queue(),您也必须对其进行重构。

于 2013-06-25T15:31:15.497 回答
0

我会选择 one NSOperation,因为您想做的 3 个操作实际上并不能独立存在。

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

于 2013-06-25T03:17:19.480 回答