83

我必须在我的应用程序中执行一系列下载和数据库写入操作。我正在使用NSOperationand NSOperationQueue

这是应用场景:

  • 从一个地方获取所有邮政编码。
  • 对于每个邮政编码,获取所有房屋。
  • 为每所房子获取居民详细信息

如前所述,我NSOperation为每个任务定义了一个。在第一种情况下(Task1),我向服务器发送请求以获取所有邮政编码。内部的委托NSOperation将接收数据。然后将此数据写入数据库。数据库操作在不同的类中定义。从NSOperation类中,我正在调用数据库类中定义的写入函数。

我的问题是数据库写操作是发生在主线程还是后台线程?当我在 a 中调用它时, NSOperation我期望它在不同的线程(不是 MainThread)中运行NSOperation。有人可以在处理NSOperationand时解释这种情况吗NSOperationQueue

4

6 回答 6

178

我的问题是数据库写操作是发生在主线程还是后台线程?

NSOperationQueue如果您从头开始创建:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];

它将在后台线程中:

操作队列通常提供用于运行其操作的线程。在 OS X v10.6 及更高版本中,操作队列使用 libdispatch 库(也称为 Grand Central Dispatch)来启动其操作的执行。因此,操作总是在单独的线程上执行,无论它们被指定为并发操作还是非并发操作

除非您使用的是mainQueue

NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

你也可以看到这样的代码:

NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
[myQueue addOperationWithBlock:^{

   // Background work

    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        // Main thread work (UI usually)
    }];
}];

和 GCD 版本:

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void)
             {
              // Background work            
             dispatch_async(dispatch_get_main_queue(), ^(void)
              {
                   // Main thread work (UI usually)                          
              });
});

NSOperationQueue更好地控制您想要做的事情。您可以在两个操作之间创建依赖关系(下载并保存到数据库)。要在一个块和另一个块之间传递数据,您可以假设 aNSData将来自服务器,因此:

__block NSData *dataFromServer = nil;
NSBlockOperation *downloadOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakDownloadOperation = downloadOperation;

[weakDownloadOperation addExecutionBlock:^{
 // Download your stuff  
 // Finally put it on the right place: 
 dataFromServer = ....
 }];

NSBlockOperation *saveToDataBaseOperation = [[NSBlockOperation alloc] init];
__weak NSBlockOperation *weakSaveToDataBaseOperation = saveToDataBaseOperation;

 [weakSaveToDataBaseOperation addExecutionBlock:^{
 // Work with your NSData instance
 // Save your stuff
 }];

[saveToDataBaseOperation addDependency:downloadOperation];

[myQueue addOperation:saveToDataBaseOperation];
[myQueue addOperation:downloadOperation];

编辑:为什么我使用__weak操作参考,可以在这里找到。但简而言之就是避免保留循环。

于 2013-11-02T21:07:08.113 回答
17

如果要在后台线程中执行数据库写入操作,则需要NSManagedObjectContext为该线程创建一个。

NSManagedObjectContext您可以在相关子类的 start 方法中创建背景NSOperation

检查 Apple 文档以了解Concurrency with Core Data。

您还可以创建一个NSManagedObjectContext在其自己的后台线程中执行请求的方法,方法NSPrivateQueueConcurrencyType是在其performBlock:方法中创建并执行请求。

于 2013-10-30T09:31:57.750 回答
11

来自NSOperationQueue

在 iOS 4 及更高版本中,操作队列使用 Grand Central Dispatch 来执行操作。在 iOS 4 之前,它们为非并发操作创建单独的线程,并从当前线程启动并发操作。

所以,

[NSOperationQueue mainQueue] // added operations execute on main thread
[NSOperationQueue new] // post-iOS4, guaranteed to be not the main thread

在您的情况下,您可能希望通过子类化创建自己的“数据库线程”NSThread并使用performSelector:onThread:.

于 2013-11-01T13:32:27.270 回答
10

NSOperation 的执行线程取决于NSOperationQueue您添加操作的位置。在您的代码中查看此语句 -

[[NSOperationQueue mainQueue] addOperation:yourOperation]; // or any other similar add method of NSOperationQueue class

所有这一切都假设您没有在方法中进行任何进一步的线程化,该main方法NSOperation是您(预期)编写的工作说明的实际怪物。

但是,在并发操作的情况下,情况就不同了。队列可以为每个并发操作生成一个线程。虽然它不是保证的,它取决于系统资源与系统中该点的操作资源需求。您可以通过它的属性来控制操作队列的并发maxConcurrentOperationCount性。

编辑-

我发现您的问题很有趣,并自己做了一些分析/记录。我在这样的主线程上创建了 NSOperationQueue -

self.queueSendMessageOperation = [[[NSOperationQueue alloc] init] autorelease];

NSLog(@"Operation queue creation. current thread = %@ \n main thread = %@", [NSThread currentThread], [NSThread mainThread]);
self.queueSendMessageOperation.maxConcurrentOperationCount = 1; // restrict concurrency

然后,我继续创建一个 NSOperation 并使用 addOperation 添加它。当我检查当前线程时,在此操作的主要方法中,

NSLog(@"Operation obj =  %@\n current thread = %@ \n main thread = %@", self, [NSThread currentThread], [NSThread mainThread]);

它不是主线程。并且,发现当前线程对象不是主线程对象。

因此,在主线程上自定义创建队列(其操作之间没有并发)并不一定意味着操作将在主线程本身上连续执行。

于 2013-10-30T05:07:37.520 回答
2

文档的摘要是operations are always executed on a separate thread(iOS 4 后暗示 GCD 底层操作队列)。

检查它是否确实在非主线程上运行很简单:

NSLog(@"main thread? %@", [NSThread isMainThread] ? @"YES" : @"NO");

在线程中运行时,使用 GCD/libdispatch 在主线程上运行某些东西是微不足道的,无论是核心数据、用户界面还是在主线程上运行所需的其他代码:

dispatch_async(dispatch_get_main_queue(), ^{
    // this is now running on the main thread
});
于 2013-11-02T19:52:00.000 回答
-2

如果您正在做任何重要的线程,您应该使用FMDatabaseQueue

于 2013-10-30T02:10:45.527 回答