Detailed use of multithreading

Keywords: github

There are several uses of multithreading in the blog reproduced in the previous article, but the introduction of NSOperation is not detailed enough. Here is a supplement.
NSOperation itself can not be directly used for operation. It needs to inherit the corresponding method of NSOperation or use NSBlockOperation and NSInvocation Operation directly to work properly. Following is the explanation from three aspects: definition, NSBlock Operation and NSInvocation Operation.
1. Customization

//
//  PHOperation.h
//  OperationTest
//
//  Created by Item Pricewater on 2017/2/9.
//  E-mail: xiangpuhua@126.com
//  Tel: +86 13316987488
//  Home page: https://github.com/xphaijj
//  Copyright 2017 Xiangpuhua. All rights reserved.
//

#import <Foundation/Foundation.h>

typedef void(^DownloadBlock)(id file);

@interface PHOperation : NSOperation

/**
 *  Threads perform download tasks
 *
 *  @param urlString     Address to download
 *  @param downloadBlock Callback completed by download
 *
 *  @return Instances of the current class
 */
- (id)initWithDownloadURL:(NSString *)urlString
                      block:(DownloadBlock)downloadBlock;


@end
//
//  PHOperation.m
//  OperationTest
//
//  Created by Item Pricewater on 2017/2/9.
//  E-mail: xiangpuhua@126.com
//  Tel: +86 13316987488
//  Home page: https://github.com/xphaijj
//  Copyright 2017 Xiangpuhua. All rights reserved.
//

#import "PHOperation.h"

@interface PHOperation () {

}

@property (nonatomic, strong) NSString *urlStr;
@property (nonatomic, copy) DownloadBlock block;

@end

@implementation PHOperation

/**
 *  Threads perform download tasks
 *
 *  @param urlString     Address to download
 *  @param downloadBlock Callback completed by download
 *
 *  @return Instances of the current class
 */
- (id)initWithDownloadURL:(NSString *)urlString
                    block:(DownloadBlock)downloadBlock {
    self = [super init];
    if (self) {
        _urlStr = urlString;
        _block = downloadBlock;
    }
    return self;
}


/**
 *  Customize main method to execute thread tasks
 */
- (void)main {
    @try {
        @autoreleasepool {
            if (self.isCancelled) {
                return;
            }
            NSURL *url = [NSURL URLWithString:self.urlStr];
            NSData *data = [NSData dataWithContentsOfURL:url];
            if (self.isCancelled) {
                url = nil;
                data = nil;
                return;
            }

            if (self.block) {
                self.block(data);
            }
        }
    } @catch (NSException *exception) {
    } @finally {
    }
}

@end

The synchronous reference method is as follows:

    NSLog(@"start");

    PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
        sleep(4);
        UIImage *image = [UIImage imageWithData:file];
        NSLog(@"%@ %@", image, [NSThread currentThread]);
    }];
    [op start];//Perform synchronization
    NSLog(@"end");

Output results:
2017-02-09 12:31:41.320 OperationTest[4553:996304] start
2017-02-09 12:31:45.730 OperationTest[4553:996304] , {320, 353} {number = 1, name = main}
2017-02-09 12:31:45.730 OperationTest[4553:996304] end
From the output we can see that the synchronous reference is properly invoked.

The asynchronous reference method is as follows:

    NSLog(@"start");
    NSOperationQueue *phQueue = [[NSOperationQueue alloc] init];

    PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
        sleep(4);
        UIImage *image = [UIImage imageWithData:file];
        NSLog(@"%@ %@", image, [NSThread currentThread]);
    }];
    [phQueue addOperation:op];//Perform asynchronous operations
    NSLog(@"end");

Output results:
2017-02-09 12:28:57.248 OperationTest[4493:979978] start
2017-02-09 12:28:57.249 OperationTest[4493:979978] end
2017-02-09 12:29:01.601 OperationTest[4493:980384] , {320, 353} {number = 5, name = (null)}

From the output we can see that the asynchronous reference is properly invoked.

    NSLog(@"start");
    PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
        sleep(4);
        UIImage *image = [UIImage imageWithData:file];
        NSLog(@"1 %@ %@", image, [NSThread currentThread]);
    }];
    PHOperation *op1 = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
        sleep(2);
        UIImage *image = [UIImage imageWithData:file];
        NSLog(@"2 %@ %@", image, [NSThread currentThread]);
    }];
    [op start];
    [op1 start];
    NSLog(@"end");

Output results:
2017-02-09 13:09:52.308 OperationTest[5420:1208386] start
2017-02-09 13:09:56.622 OperationTest[5420:1208386] 1 , {320, 353} {number = 1, name = main}
2017-02-09 13:09:58.852 OperationTest[5420:1208386] 2 , {320, 353} {number = 1, name = main}
2017-02-09 13:09:58.853 OperationTest[5420:1208386] end
The output shows that op and op1 are synchronized.

    NSLog(@"start");
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    PHOperation *op = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
        sleep(4);
        UIImage *image = [UIImage imageWithData:file];
        NSLog(@"1 %@ %@", image, [NSThread currentThread]);
    }];
    PHOperation *op1 = [[PHOperation alloc] initWithDownloadURL:@"https://www.lsd1888.com/res/21/57e77030f3d1d.jpg" block:^(id file) {
        sleep(2);
        UIImage *image = [UIImage imageWithData:file];
        NSLog(@"2 %@ %@", image, [NSThread currentThread]);
    }];
    [queue addOperation:op];
    [queue addOperation:op1];
    NSLog(@"end");

Output results:
2017-02-09 13:13:20.089 OperationTest[5510:1234538] start
2017-02-09 13:13:20.090 OperationTest[5510:1234538] end
2017-02-09 13:13:22.440 OperationTest[5510:1234706] 2 , {320, 353} {number = 5, name = (null)}
2017-02-09 13:13:24.534 OperationTest[5510:1234688] 1 , {320, 353} {number = 6, name = (null)}
From the output, it can be seen that the execution between op and op1 is asynchronous.

2,NSBlockOperation

    NSLog(@"start");
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(4);
        NSLog(@"op1");
    }];
    [op1 start];//Synchronous execution
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"op2");
    }];
    [op2 start];//Synchronous execution
    NSLog(@"end");

Output results:
2017-02-09 12:46:57.590 OperationTest[4835:1073639] start
2017-02-09 12:47:01.684 OperationTest[4835:1073639] op1
2017-02-09 12:47:01.684 OperationTest[4835:1073639] op2
2017-02-09 12:47:01.685 OperationTest[4835:1073639] end
The output results show that the above methods are implemented synchronously.

    NSLog(@"start");
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(4);
        NSLog(@"op1");
    }];
    [op1 start];//Synchronous execution
    NSBlockOperation *op2 = [[NSBlockOperation alloc] init];
    [op2 addExecutionBlock:^{//Asynchronous operation
        sleep(3);
        NSLog(@"start");
    }];
    [op2 addExecutionBlock:^{
        sleep(1);
        NSLog(@"middle");
    }];
    [op2 addExecutionBlock:^{
        sleep(2);
        NSLog(@"end1");
    }];
    [op2 setCompletionBlock:^{
        NSLog(@"op2 done");
    }];
    [op2 start];//Synchronous execution
    NSLog(@"end");

Output results:
2017-02-09 12:51:41.925 OperationTest[4975:1102448] start
2017-02-09 12:51:45.997 OperationTest[4975:1102448] op1
2017-02-09 12:51:47.072 OperationTest[4975:1102542] middle
2017-02-09 12:51:48.070 OperationTest[4975:1102539] end1
2017-02-09 12:51:49.073 OperationTest[4975:1102448] start
2017-02-09 12:51:49.073 OperationTest[4975:1102448] end
2017-02-09 12:51:49.073 OperationTest[4975:1102539] op2 done
From the above results, we can see that op1 and op2 are executed synchronously, and op2 is executed asynchronously.

    NSLog(@"start");
    NSOperationQueue *queueBlock = [[NSOperationQueue alloc] init];
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        sleep(2);
        NSLog(@"op1");
    }];
    NSBlockOperation *op2 = [[NSBlockOperation alloc] init];
    [op2 addExecutionBlock:^{//Asynchronous operation
        sleep(3);
        NSLog(@"start");
    }];
    [op2 addExecutionBlock:^{
        sleep(1);
        NSLog(@"middle");
    }];
    [op2 addExecutionBlock:^{
        sleep(2);
        NSLog(@"end1");
    }];
    [op2 setCompletionBlock:^{
        NSLog(@"op2 done");
    }];
    [queueBlock addOperation:op1];
    [queueBlock addOperation:op2];
    NSLog(@"end");

Implementation results:
2017-02-09 12:54:27.294 OperationTest[5105:1121668] start
2017-02-09 12:54:27.295 OperationTest[5105:1121668] end
2017-02-09 12:54:28.368 OperationTest[5105:1122096] middle
2017-02-09 12:54:29.368 OperationTest[5105:1122113] op1
2017-02-09 12:54:29.369 OperationTest[5105:1122086] end1
2017-02-09 12:54:30.370 OperationTest[5105:1122090] start
2017-02-09 12:54:30.370 OperationTest[5105:1122113] op2 done
From the output results, it can be seen that op1 and op2 are executed asynchronously, and op2 is executed asynchronously.

3,NSInvocationOperation

    NSLog(@"start");
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"1"];
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"2"];
    [operation start];
    [operation2 start];
    NSLog(@"end");

Output results:
2017-02-09 12:58:43.835 OperationTest[5209:1144896] start
2017-02-09 12:58:47.909 OperationTest[5209:1144896] Output parameters after 4 seconds of dormancy:1
2017-02-09 12:58:51.984 OperationTest[5209:1144896] Output parameters after 4 seconds of dormancy:2
2017-02-09 12:58:51.985 OperationTest[5209:1144896] end
According to the output, operation and operation 2 are executed synchronously.

    NSLog(@"start");
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];//Direct asynchronous operation
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"4"];
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(doPo:) object:@"3"];
    [queue addOperation:operation];
    [queue addOperation:operation2];
    NSLog(@"end");

Output results:
2017-02-09 13:01:19.211 OperationTest[5275:1160990] start
2017-02-09 13:01:19.213 OperationTest[5275:1160990] end
2017-02-09 13:01:22.287 OperationTest[5275:1161244] Output parameters after 3 seconds of dormancy:3
2017-02-09 13:01:23.288 OperationTest[5275:1161249] Output parameters after 4 seconds of dormancy:4
According to the output results, operation and operation 2 are executed asynchronously.

To sum up: To implement a subclass of NSOperation, the instance itself calls the start method to execute the synchronous method, while the asynchronous method is executed after joining the execution queue.

Posted by Chrisj on Mon, 25 Mar 2019 15:21:30 -0700