Painted painted levels: fostered fostered fostered

Tags: “iOS” “multi-threading” “NSThread” author: Dac_1033 Review: QiShare team

IOS multithreading series plan 3, respectively: NSThread, NSOperation, GCD.

This is the first article in a series.

1. The concept of threads

To begin with, each program we run independently on a computer is an independent process, usually independent of each other. A process is the smallest unit of resources allocated by the system. The smallest execution unit in a process is thread, and there is at least one thread in a process, all threads in the process share the resources of this process, thread is also the smallest unit of system scheduling.

1.1 Multithreading Processes tasks on a multi-core CPU

1.2 Thread state switching

1.3 Thread safety

Thread-safety issues arise when multiple threads are working on tasks. For example, if multiple threads execute the code (task) at the same time, when multiple threads execute count– in the getTicket method at the same time, the count result will be inaccurate, and the process is not thread-safe.

NSInteger ticketCount = 100; - (void)getTicket() { count--; NSLog(@" remaining votes = %d", ticketCount); }Copy the code
1.4 Multithreading in iOS

In iOS, a main thread, also known as the UI thread, is created after each app starts. Since all child threads other than the main thread are independent of Cocoa Touch, only the main thread is generally used to update the UI. There are three modes of multithreading in iOS: NSThread, NSOperation, and GCD, among which GCD is currently recommended by Apple. For this article, we’ll focus on NSThreads.

2. NSThread profile

Nsthreads are lightweight multithreaded development. The advantage is that you can instantiate an NSThread object directly and manipulate it directly, but using NSThreads requires managing the thread lifecycle yourself. During iOS development, the most commonly used method of NSThread is [NSThread currentThread] to obtain the currentThread. Other commonly used attributes and methods are as follows:

@property (readonly, retain) NSMutableDictionary *threadDictionary; @property (nullable, copy) NSString *name; @property double threadPriority; @property (readonly) BOOL isMainThread // Reads thread status. @property (readonly, getter=isExecuting) BOOL Executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isCancelled) BOOL cancelled; // add the action directly to the thread and start + (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument // create a thread object - (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument // start - (void)start; // Cancel - (void)cancel; // exit + (void)exit; // sleep + (void)sleepForTimeInterval:(NSTimeInterval)ti;Copy the code

Several common methods in the NSObject(NSThreadPerformAdditions) class implement tasks on a specific thread, which is also defined in nsThread.h:

/ / in the main thread to execute a method - (void) performSelectorOnMainThread: (SEL) aSelector withObject: (nullable id) arg waitUntilDone: (BOOL) wait; // Execute a method on the specified thread, You need to create a thread object - (void)performSelector:(SEL)aSelector onThread:(NSThread *) THR withObject:(nullable id)arg waitUntilDone:(BOOL)wait; / / perform an operation in the background, essence is to create a thread to perform current method - (void) performSelectorInBackground aSelector: (SEL) withObject: (arg nullable id);Copy the code
2.1 Usage process of NSThread

For example, when the app downloads pictures from the network, it may take a long time due to network reasons. In this case, if the download is only carried out in the main thread, the user will not be able to carry out other operations during this process, and the interface will be stuck (thread blocking) until the downloading of pictures from the network is completed. We can solve this problem by creating a new thread in the main thread to download the resource separately. Regardless of whether the resource has been downloaded or not, the interface can continue to operate without blocking. Example code is as follows:

@interface MultiThread_NSThread ()

// 显示图片
@property (nonatomic, strong) UIImageView *imgView;

@end

@implementation MultiThread_NSThread

- (void)viewDidLoad {
    
    [super viewDidLoad];
    [self setTitle:@"NSThread"];
    [self.view setBackgroundColor:[UIColor whiteColor]];
    self.edgesForExtendedLayout = UIRectEdgeNone;
    
    [self layoutViews];
}

- (void)layoutViews {
    
    CGSize size = self.view.frame.size;
    
    _imgView =[[UIImageView alloc] initWithFrame:CGRectMake(0, 0, size.width, 300)];
    [self.view addSubview:_imgView];
    
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    button.frame = CGRectMake(15, CGRectGetMaxY(_imgView.frame) + 30, size.width - 15 * 2, 45);
    [button setTitle:@"点击加载" forState:UIControlStateNormal];
    [button addTarget:self action:@selector(loadImageWithMultiThread) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:button];
}


#pragma mark - 多线程下载图片

- (void)loadImageWithMultiThread {
    
    ////1. 对象方法
    //NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(loadImage) object:nil];
    //[thread start];
    
    //2. 类方法
    [NSThread detachNewThreadSelector:@selector(downloadImg) toTarget:self withObject:nil];
}


#pragma mark - 加载图片

- (void)downloadImg {
    
    // 请求数据
    NSData *data = [self requestData];
    // 回到主线程更新UI
    [self performSelectorOnMainThread:@selector(updateImg:) withObject:data waitUntilDone:YES];
}


#pragma mark - 请求图片数据

- (NSData *)requestData {
    
    NSURL *url = [NSURL URLWithString:@"https://store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/image/AppleInc/aos/published/images/a/pp/apple/products/apple-products-section1-one-holiday-201811?wid=2560&hei=1046&fmt=jpeg&qlt=95&op_usm=0.5,0.5&.v=1540576114151"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    return data;
}


#pragma mark - 将图片显示到界面

- (void)updateImg:(NSData *)imageData {
    
    UIImage *image = [UIImage imageWithData:imageData];
    _imgView.image = image;
}

@end
Copy the code

Make a breakpoint on the code requesting data to see that NSThread encapsulates pthread:

2.2 Using NSThread to achieve multi-threaded concurrency

Below, we use NSThread to realize multithreading to load multiple network pictures to understand the process of NSThread multithreading task processing. Example code is as follows:

@implementation NSThreadImage @end #define ColumnCount 4 #define RowCount 5 #define Margin 10 @interface MultiThread_NSThread1 () // imgView array@property (nonatomic, strong) NSMutableArray *imgViewArr; // Thread array @property (nonatomic, strong) NSMutableArray *threadArr; @end @implementation MultiThread_NSThread1 - (void)viewDidLoad { [super viewDidLoad]; [self setTitle:@"NSThread1"]; [self.view setBackgroundColor:[UIColor whiteColor]]; self.edgesForExtendedLayout = UIRectEdgeNone; [self layoutViews]; } - (void)layoutViews { CGSize size = self.view.frame.size; CGFloat imgWidth = (size.width - Margin * (ColumnCount + 1)) / ColumnCount; _imgViewArr = [NSMutableArray array]; for (int row=0; row<RowCount; row++) { for (int colomn=0; colomn<ColumnCount; colomn++) { UIImageView *imageView=[[UIImageView alloc] initWithFrame:CGRectMake(Margin + colomn * (imgWidth + Margin), Margin + row * (imgWidth + Margin), imgWidth, imgWidth)]; imageView.backgroundColor=[UIColor cyanColor]; [self.view addSubview:imageView]; [_imgViewArr addObject:imageView]; } } UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem]; button.frame = CGRectMake(15, (imgWidth + Margin) * RowCount + Margin, size.width - 15 * 2, 45); [button addTarget:self action:@selector(loadImgWithMultiThread) forControlEvents:UIControlEventTouchUpInside]; [button setTitle: @ "click load" forState: UIControlStateNormal]; [self.view addSubview:button]; } #pragma mark - (void)loadImgWithMultiThread {_threadArr = [NSMutableArray array]; for (int i=0; i<RowCount*ColumnCount; ++i) { NSThreadImage *threadImg = [[NSThreadImage alloc] init]; threadImg.index = i; NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(loadImg:) object:threadImg]; thread.name = [NSString stringWithFormat:@"myThread%i",i]; //// Priority //thread.threadPriority = 1.0; [thread start]; [_threadArr addObject:thread]; }} #pragma mark - (void)loadImg:(NSThreadImage *)threadImg {//// sleepForTimeInterval:2.0]; //// cancel (stop loading image) //[[NSThread currentThread] cancel]; //// exit the current thread //[NSThread exit]; ImgData = [self requestData]; / / back to the main thread to update the UI [self performSelectorOnMainThread: @ the selector (updateImg:) withObject: threadImg waitUntilDone: YES]; // Print the current thread NSLog(@"current thread: %@", [NSThread currentThread]); } #pragma mark - (NSData *)requestData{NSURL *url = [NSURL *url URLWithString:@"https://store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/image/AppleInc/aos/published/images/ A/pp/apple/products/apple - products - section1 - one - holiday - 201811? Wid = 2560 & hei = 1046 & FMT = jpeg&qlt = 95 & op_usm = 0.5, 0.5 &. V = 15405 76114151 "]; NSData *data = [NSData dataWithContentsOfURL:url]; return data; } #pragma mark - (void)updateImg:(NSThreadImage *)threadImg {UIImage *image = [UIImage imageWithData:threadImg.imgData]; UIImageView *imageView = _imgViewArr[threadImg.index]; imageView.image = image; // //- (void)stopLoadingImgs {// // for (int I =0; i<RowCount*ColumnCount; ++i) { // // NSThread *thread = _threadArr[i]; // if (! thread.isFinished) { // [thread cancel]; // } // } //} @endCopy the code

3. Description about the status of NSThreads

NSThread objects can obtain isExecuting, isFinished, and isCancellled state properties of a thread. The undo state can be set manually in code by calling the thread’s cancel method (you can’t actually stop the current thread in the main thread). A thread simply cancels the execution of a task on the current thread. Checking isFinished = YES or calling cancel does not immediately exit the thread. The exit method is called to exit the current thread immediately.

For example, when loading multiple network images, stop the execution of loading action halfway:

- (void)stopLoadingImgs {for (int I =0; i<RowCount*ColumnCount; ++i) { NSThread *thread = _threadArr[i]; if (! thread.isFinished) { [thread cancel]; }}}Copy the code

PS:

  1. UI update needs to return to the main thread operation;
  2. When a thread is in a ready state, it is in a wait state and may not execute immediately.
  3. Distinguish the difference of three states of thread, especially the difference of undo and exit two states;
  4. After the thread dies, click the screen again to try to restart the thread, the application will hang;
  5. NSThread sets the priority of thread.threadPriority. The value of threadPriority ranges from 0 to 1.
  6. Nsthreads do not provide a method to set the dependency between threads, so the order of task processing cannot be set solely by nsthreads. However, we can optimize the order of task processing as much as possible by setting the sleep or priority of NSthreads.
  7. In our experiment project, although the number of NSThread instances is theoretically unlimited, the number of threads needs to be controlled during normal processing.

Project source GitHub address

Recommended articles:

IOS signature mechanism iOS scan qr code/bar code iOS understand Xcode Bitcode iOS drawRect strange dance weekly