NSOperation and NSOperationQueue working thread vs main thread

后端 未结 6 1996
心在旅途
心在旅途 2020-11-28 01:00

I have to carry out a series of download and database write operations in my app. I am using the NSOperation and NSOperationQueue for the same.

相关标签:
6条回答
  • 2020-11-28 01:35

    If you want to perform the database writing operation in the background thread you need to create a NSManagedObjectContext for that thread.

    You can create the background NSManagedObjectContext in the start method of your relevant NSOperation subclass.

    Check the Apple docs for Concurrency with Core Data.

    You can also create an NSManagedObjectContext that executes requests in its own background thread by creating it with NSPrivateQueueConcurrencyType and performing the requests inside its performBlock: method.

    0 讨论(0)
  • 2020-11-28 01:46

    My question is whether the database write operation occur in main thread or in a background thread?

    If you create an NSOperationQueue from scratch as in:

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

    It will be in a background thread:

    Operation queues usually provide the threads used to run their operations. In OS X v10.6 and later, operation queues use the libdispatch library (also known as Grand Central Dispatch) to initiate the execution of their operations. As a result, operations are always executed on a separate thread, regardless of whether they are designated as concurrent or non-concurrent operations

    Unless you are using the mainQueue:

    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
    

    You can also see code like this:

    NSOperationQueue *myQueue = [[NSOperationQueue alloc] init];
    [myQueue addOperationWithBlock:^{
    
       // Background work
    
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            // Main thread work (UI usually)
        }];
    }];
    

    And the GCD version:

    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 gives finer control with what you want to do. You can create dependencies between the two operations (download and save to database). To pass the data between one block and the other, you can assume for example, that a NSData will be coming from the server so:

    __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];
    

    Edit: Why I am using __weak reference for the Operations, can be found here. But in a nutshell is to avoid retain cycles.

    0 讨论(0)
  • 2020-11-28 01:46

    The execution thread of NSOperation depends on the NSOperationQueue where you added the operation. Look out this statement in your code -

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

    All this assumes you have not done any further threading in main method of NSOperation which is the actual monster where the work instructions you have (expected to be) written.

    However, in case of concurrent operations, the scenario is different. The queue may spawn a thread for each concurrent operation. Although it's not guarrantteed and it depends on system resources vs operation resource demands at that point in the system. You can control concurrency of operation queue by it's maxConcurrentOperationCount property.

    EDIT -

    I found your question interesting and did some analysis/logging myself. I have NSOperationQueue created on main thread like this -

    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
    

    And then, I went on to create an NSOperation and added it using addOperation. In the main method of this operation when i checked for current thread,

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

    it was not as main thread. And, found that current thread object is not main thread object.

    So, custom creation of queue on main thread (with no concurrency among its operation) doesn't necessarily mean the operations will execute serially on main thread itself.

    0 讨论(0)
  • 2020-11-28 01:48

    The summary from the docs is operations are always executed on a separate thread (post iOS 4 implies GCD underlying operation queues).

    It's trivial to check that it is indeed running on a non-main thread:

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

    When running in a thread it's trivial to use GCD/libdispatch to run something on the main thread, whether core data, user interface or other code required to run on the main thread:

    dispatch_async(dispatch_get_main_queue(), ^{
        // this is now running on the main thread
    });
    
    0 讨论(0)
  • 2020-11-28 01:52

    From NSOperationQueue

    In iOS 4 and later, operation queues use Grand Central Dispatch to execute operations. Prior to iOS 4, they create separate threads for non-concurrent operations and launch concurrent operations from the current thread.

    So,

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

    In your case, you might want to create your own "database thread" by subclassing NSThread and send messages to it with performSelector:onThread:.

    0 讨论(0)
  • 2020-11-28 01:56

    If you're doing any non-trivial threading, you should use FMDatabaseQueue.

    0 讨论(0)
提交回复
热议问题