0

I am new to multithreading in iOS. I need to do three things: get information from the api, parse the information and save to my database. I have these three things in a different files(getAPI,parseAPI and savetoDB). getAPI will call parseAPI and it will in return call savetoDB. I want all three of them to work in background thread.

My question is when I call getAPI, will parseAPI and savetoDB run in the background thread as well? How do I ensure that all three of them run in the background? How do I return the call back to main thread after savetoDB?

Example:

dispatch_queue_t backgroundQueue;
backgroundQueue = dispatch_queue_create("lakesh", NULL);  
- (void)startprocess {    
    dispatch_async(backgroundQueue, ^(void) {
        [self getAPI];
    });    
}

Need some guidance.. Thanks...

4

3 回答 3

2

如果您在后台线程上发出一个函数,则所有执行都将在该线程上继续,直到它完成,或者您在主线程上回调另一个函数。一开始我也和你一样担心,所以我给自己做了以下宏:

/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
    NSAssert(![NSThread isMainThread], @"Don't block the UI thread please!")

/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
    NSAssert([NSThread isMainThread], @"You aren't running in the UI thread!")

正如您在评论中看到的那样,我倾向于在方法的开头使用这些宏,以确保我不会在错误的线程中错误地使用这些宏。我已将这些宏和更多随机内容放在https://github.com/gradha/ELHASO-iOS-snippets,您可能会发现它们很有用。

关于您返回主线程的问题,因为您使用的是 GCD,所以最好dispatch_get_main_queue()在最后调用savetoDB您想要在那里运行的代码。如果savetoDB是一个库函数,它的入口点应该允许在一切完成后传入您想要在主线程上运行的成功块。这是https://github.com/AFNetworking/AFNetworking等库使用的模式。请注意他们的示例如何提供一个 API,其中的东西在后台运行,然后您的代码被回调(通常在主线程中)。

于 2013-02-09T13:26:53.450 回答
1

是的,当然如果getAPI调用parseAPI,代码parseAPI将在与执行的线程相同的线程上getAPI执行,因此在您的示例中,在后台队列中。

要在最后将回调返回到主线程,请使用与 Apple 使用的相同技术,completionBlock您可以在多个 Apple API 上看到它们:只需将一个块(例如dispatch_block_t,或void(^)(NSError*)任何适合您需要的)作为参数传递给您的getAPI:方法,这将将它传递给parseAPI:它将依次传递给它,savetoDB:最后savetoDB:可以简单地用于dipatch_async(dispatch_get_main_queue, completionBlock);在主线程上调用此代码块(从方法传递到方法)。

注意:对于您的 getAPI,您可以使用 Apple 的sendAsynchronousRequest:queue:completionHandler:方法,该方法将在后台自动执行请求,然后调用指示的完成块NSOperationQueueNSOperationQueue内部使用 GCD 的 dispatch_queue)。有关更多信息,请参阅有关NSOperationQueueGCD 和并发编程指南的文档以及 Apple 文档中所有非常详细的指南。


-(void)getAPI:( void(^)(NSError*) )completionBlock
{
  NSURLRequest* req = ...
  NSOperationQueue* queue = [[NSOperationQueue alloc] init]; // the completionHandler will execute on this background queue once the network request is done
  [NSURLConnection sendAsynchronousRequest:req queue:queue completionHandler:^(NSURLResponse* resp, NSData* data, NSError* error)
   {
     if (error) {
       // Error occurred, call completionBlock with error on main thread
       dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
     } else {
       [... parseAPI:data completion:completionBlock];
     }
   }];
}

-(void)parseAPI:(NSData*)dataToParse completion:( void(^)(NSError*) )completionBlock
{
   ... parse datatToParse ...

   if (parsingError) {
     dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(error); });
   } else {
     [... savetoDB:dataToSave completion:completionBlock];
   }
}

-(void)savetoDB:(id)dataToSave completion:( void(^)(NSError*) )completionBlock
{
   ... save to your DB ...

   // Then call the completionBlock on main queue / main thread
   dispatch_async(dispatch_get_main_queue(), ^{ completionBlock(dbError); }); // dbError may be nil if no error occurred of course, that will tell you everything worked fine
}

-(void)test
{
  [... getAPI:^(NSError* err)
   {
      // this code will be called on the main queue (main thread)
      // err will be nil if everythg went OK and vontain the error otherwise
   }];
}
于 2013-02-09T13:15:25.577 回答
1

是的,parseAPIsavetoDB在您创建的新队列中运行。如果您需要在操作完成后修改 UI,则该代码必须在主线程中运行。为此,请获取对主队列的引用并向其发送一些代码。例如:

- (void)startprocess {    
    dispatch_async(backgroundQueue, ^(void) {
        [self getAPI];
        dispatch_async(dispatch_get_main_queue(), ^{
             // Refresh the UI with the new information
        });
    });    
}

完成后不要忘记加入dispatch_release新队列。

Cocoa 本身在框架的许多部分中使用的另一种模式是在后台操作结束时调用的 API 函数的签名中添加回调块。这个 Stack Overflow 线程解释了如何做到这一点。

于 2013-02-09T13:16:32.543 回答