【问题标题】:Execute methods which call each other. All in background执行相互调用的方法。全部在后台
【发布时间】:2013-06-25 03:08:59
【问题描述】:

在我的应用程序中,我很少有方法相互调用来解析 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 类,我的意思是一个用于startParsing,一个用于traverseXML,等等?或者只是创建一个NSOperation 子类并在里面做所有的实现方法就足够了。

【问题讨论】:

  • 顺便说一句,我没想到上面的代码示例会阻塞您的主线程(这是 AFNetworking 的乐趣之一)。你是说有吗?
  • 是的,它阻塞了主线程,我的应用程序被冻结了很长时间,直到所有 XML 提要都被完全解析。

标签: ios concurrency nsoperation nsoperationqueue


【解决方案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 以获得很好的解释。假设我将采用NSOperation,是否应该将traverseXMLsaveFile的代码放在一个方法中的NSOperation子类中,然后在实例化这个类时调用它?
  • @Malloc 如果你使用NSOperation 子类,你要么必须把代码放在你的操作类中,要么你需要传递一个它会调用的块。您可能会使用标准的 NSOperation start 方法来启动解析。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    
        //parse
        //traverse
        //save
    
        dispatch_async (dispatch_get_main_queue (), ^{
    
            //update UI
        });
    });
    

    【讨论】:

      【解决方案4】:

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

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

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

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-03-21
        • 2014-05-16
        • 2018-10-30
        • 2020-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多