Application Background:

If it is a simple application development, using the GCD is enough, NSThread, PThread, the GCD, NSOperation, but if this is similar to the breakpoint continuingly complex business, will need to use the NSOperation, control more flexible.

Knowledge graph:

I will analyze NSOperation step by step from five aspects:

Demo address: github.com/tanghaitao/…

1. NSOperation early experience

Demo address: github.com/tanghaitao/…

According to apple’s official documentation, NSOperation needs to pay attention to the following three points:

  1. NSOperation itself is an abstract class and cannot be instantiated.
  2. You need to add anything to its subclasses NSInvocationOperation or NSBlockOperation.
  3. To execute a task, you need to add to the NSOperationQueue to execute the task.

Summary: NSOperation — transaction () + queue = cannot be used directly to add transactions to the queue –> and then execute on a new thread

NSInvationOperation

- (void)demo1{// do not use NSOperation -- transaction () + queue = add transaction to queue --> NSOperation NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(xixihha) object:nil]; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; NSThread == <NSThread: 0x6000024BD300 >{number = 4, name = (null)} [queue addOperation:op]; [op start]; [op start]; NSThread == <NSThread: 0x60000382C1C0 >{number = 1, name = main}} // Operation - (void)xixihha{NSLog(@"NSThread == %@",[NSThread currentThread]); NSLog(@"123"); }Copy the code

NSBlockOperation

- (void)demo2{// Operation priority NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"NSThread == %@",[NSThread currentThread]); // Concurrency [NSThread sleepForTimeInterval:1];// Delay 1 second NSLog(@" perform 123 task "); [[NSOperationQueue MainQueue] addOperationWithBlock:^{NSThread sleepForTimeInterval:10]; NSLog(@" update UI");}]; // 1+5<10 1+13>10 // completionBlock Curly braces start and end '{}' //[NSThread sleepForTimeInterval:13]; //NSLog(@"123");}]; // op.qualityOfService = [op addExecutionBlock:^{NSLog(@"NSThread == %@",[NSThread currentThread]); NSLog(@"456"); [NSThread sleepForTimeInterval:5]; NSLog(@"456");}]; Op.completionblock = ^{NSLog(@" execute completed task "); [NSThread sleepForTimeInterval:1]; NSLog (@ "finished"); }; NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperation:op]; // [op start]; // NSThread == <NSThread: 0x60000018c640>{number = 6, name = (null)} }Copy the code
NSThread == <NSThread: 0x6000030FeDC0 >{Number = 5, name = (null)} NSThread == <NSThread: 0x6000030FD900 >{Number = 4, Name = (null)}// Perform task 456 perform task 123 Perform task UI update // perform task 456 sleep, so mainQueue 456 sleep longer, 5 seconds later execute 456 Execute complete task //op all tasks completed, 5 seconds completed // completed, 6 seconds updated UI// 10 secondsCopy the code

Analysis: The task [op addExecutionBlock] and the task blockOperationWithBlock:^{} are concurrent, the execution order of the tasks is not fixed, and then the printing execution of 456 tasks, and then the printing execution of 123 tasks, because the main queue of tasks are in the thread of the first block, Block, so you need to wait for 123 tasks to finish before printing, updating the UI, and looking at the comments above

[[NSOperationQueue mainQueue] addOperationWithBlock:^{} Must wait until task 1 is executed '{', Op.com pletionBlock= ^{} must wait until all tasks added to the queue by op are completed, curly braces begin and end '{}','{}' is completed, Queue addOperation:op is regulated by the system; it is not the mainQueue and does not need to wait for the mainQueue to complete.Copy the code

2.NSOperation attribute research

Demo address: github.com/tanghaitao/…

2.1 maxConcurrentOperationCount

Directly with NSOperation concurrency control is very simple, set properties maxConcurrentOperationCount GCD control is quite complicated.

GCD controls the maximum number of concurrent requests:

dispatch_semaphore_t semaphore = dispatch_semaphore_create(2); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog (@ "- % @ - task 1", [NSThread currentThread]); NSLog(@" Perform task 1"); sleep(6); NSLog(@" Task 1 completed "); dispatch_semaphore_signal(semaphore); }); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog (@ "- % @ - task 2", [NSThread currentThread]); NSLog(@" Perform task 2"); sleep(5); NSLog(@" Task 2 completed "); dispatch_semaphore_signal(semaphore); }); Dispatch_async (queue, ^{dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog (@ "- % @ - task 3", [NSThread currentThread]); NSLog(@" Perform task 3"); NSLog(@" Task 3 completed "); dispatch_semaphore_signal(semaphore); });Copy the code

NSOperation controls the maximum number of concurrent requests:

/** On the suspension of operationQueue, proceed, cancel */ - (void)demo1{// GCD --> semaphore: More comfortable with threads -- suspend cancel Finish // Multithreaded world self.queue.name = @"com.haitao"; self.queue.maxConcurrentOperationCount = 2; for (int i = 0; i<10; i++) { [self.queue addOperationWithBlock:^{ [NSThread sleepForTimeInterval:2]; NSLog(@"%@--%d",[NSThread currentThread],i); }]; }}Copy the code
begin <NSThread: 0x6000038e2fc0>{number = 6, name = (null)}--1
begin <NSThread: 0x6000038a8f00>{number = 3, name = (null)}--0
end <NSThread: 0x6000038e2fc0>{number = 6, name = (null)}--1
end <NSThread: 0x6000038a8f00>{number = 3, name = (null)}--0
begin <NSThread: 0x6000038a1040>{number = 4, name = (null)}--2
begin <NSThread: 0x6000038a8f00>{number = 3, name = (null)}--3
end <NSThread: 0x6000038a8f00>{number = 3, name = (null)}--3
end <NSThread: 0x6000038a1040>{number = 4, name = (null)}--2
begin <NSThread: 0x6000038a1040>{number = 4, name = (null)}--4
begin <NSThread: 0x6000038e2fc0>{number = 6, name = (null)}--5
end <NSThread: 0x6000038a1040>{number = 4, name = (null)}--4
end <NSThread: 0x6000038e2fc0>{number = 6, name = (null)}--5
begin <NSThread: 0x6000038a1040>{number = 4, name = (null)}--7
begin <NSThread: 0x6000038a8f00>{number = 3, name = (null)}--6
end <NSThread: 0x6000038a1040>{number = 4, name = (null)}--7
end <NSThread: 0x6000038a8f00>{number = 3, name = (null)}--6
begin <NSThread: 0x6000038a1040>{number = 4, name = (null)}--8
begin <NSThread: 0x6000038e2fc0>{number = 6, name = (null)}--9
end <NSThread: 0x6000038a1040>{number = 4, name = (null)}--8
end <NSThread: 0x6000038e2fc0>{number = 6, name = (null)}--9
Copy the code

In the preceding figure, two tasks are executed each time. Begin BEGIN End End

- (IBAction)pauseOrContinue:(id)sender {// download task --> task --> suspend // continue ----> // interrupt --> background self.queue.suspended =! self.queue.isSuspended; [self. PauseOrContinueBtn setTitle: self. Queue. Suspended? @ "continue" : @ "suspended" forState: UIControlStateNormal]; If (self) queue) operationCount = = 0) {NSLog (@ "no operation"); return; } if (self.queue.suspended) {NSLog(@" currently suspended "); }else{NSLog(@" execute....") ); } // self.queue }Copy the code
begin <NSThread: 0x600000888a00>{number = 8, name = (null)}--1 begin <NSThread: 0x600000880780>{number = 6, name = (null)}--0 end <NSThread: 0x600000888a00>{number = 8, name = (null)}--1 end <NSThread: 0x600000880780>{number = 6, name = (null)}--0 begin <NSThread: 0x60000089cd80>{number = 7, name = (null)}--2 begin <NSThread: 0x600000888a00>{number = 8, name = (null)} 0x600000888a00>{number = 8, name = (null)}--3 end <NSThread: 0x60000089cd80>{number = 7, name = (null)}--2Copy the code

self.queue.suspended = ! self.queue.isSuspended; After a task is suspended, it will not be suspended or cancelled immediately. New tasks that are added will be stopped after the execution is complete.

2.3 addDependency

Op.com pletionBlock = ^{}

- (void)demo2{ self.queue.maxConcurrentOperationCount = 2; NSBlockOperation *bo1 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"begin request token"); [NSThread SleepForTimeInterval :0.5]; NSLog(@"end request token");}]; NSBlockOperation *bo2 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"begin takes token, requests data 1"); [NSThread SleepForTimeInterval :1.5]; NSLog(@"end with token, request data 1");}]; NSBlockOperation *bo3 = [NSBlockOperation blockOperationWithBlock:^{NSLog(@"begin holds data 1, requests data 2"); [NSThread SleepForTimeInterval :0.5]; NSLog(@"end with data 1, request data 2");}]; //[self.queue addOperation:bo1]; // dependency [bo2 addDependency:bo1]; [bo3 addDependency:bo2]; [self.queue addOperations:@[bo1,bo2,bo3] waitUntilFinished:NO]; NSLog at sign "done? I'll do something else "); }Copy the code
Done? //waitUntilFinished:NO, if YES, is blocked, End Requests token BEGIN Holds the token and requests data 1 end holds the token and requests data 1 Begin Holds data 1 and requests data 2 End holds data 1 and requests data 2Copy the code

3. NSOperation cache scheme and precautions

Demo address: github.com/tanghaitao/…

__weak typeof(self) weakSelf = self; self.viewModel = [[KCViewModel alloc] initWithBlock:^(id data) { [weakSelf.dataArray addObjectsFromArray:data]; [weakSelf.collectionView reloadData]; } fail:nil]; // It is better to delay the operation of the dispatch_after child thread and not affect the main thread and start loadingCopy the code
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{ %@",model.title); NSURL *url = [NSURL URLWithString:model.imageUrl]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data];// Data will not crash if data is nil addOperationWithBlock:^{ cell.imageView.image = image; }]; }];Copy the code

Printout:

2021-05-26 10:59:05.004338+0800 003-- Custom NSOperation[14013:11296141] 2021-05-26 10:59:05.005366+0800 003-- Custom NSOperation[14013:11296143] download image: 2021-05-26 10:59:05.005908+0800 003-- Custom NSOperation[14013:11296146] 2021-05-26 10:59:05.006446+0800 003-- Custom NSOperation[14013:11296144] Customize NSOperation[14013:11296149] 2021-05-26 10:59:05.007565+0800 003-- Custom NSOperation[14013:11296147] 2021-05-26 10:59:05.008052+0800 003-- Custom NSOperation[14013:11296142] 2021-05-26 10:59:05.008553+0800 003-- Custom NSOperation[14013:11296151] 2021-05-26 10:59:05.150593+0800 003-- Custom NSOperation[14013:11296154] [] Nw_PROTOCOL_GET_quic_image_BLOCK_invoke Dlopen libquic failed 2021-05-26 10:59:07.123287+0800 003-- NSOperation[14013:11296151] 2021-05-26 10:59:07.141981+0800 003-- Custom NSOperation[14013:11296142] 2021-05-26 10:59:07.158524+0800 003-- Custom NSOperation[14013:11296154] 2021-05-26 10:59:07.175219+0800 003-- Custom NSOperation[14013:11296150] 2021-05-26 10:59:07.220150+0800 003-- Custom NSOperation[14013:11296168] 2021-05-26 10:59:07.237240+0800 003-- Custom NSOperation[14013:11296155] download picture: 2021-05-26 10:59:07.253587+0800 003-- Custom NSOperation[14013:11296166] Contracted Dog 2021-05-26 10:59:07.269462+0800 003-- Custom NSOperation[14013:11296143] Contracted Dog 2021-05-26 10:59:07.302763+0800 003-- Custom NSOperation[14013:11296170] Contracted Dog 2021-05-26 10:59:07.319611+0800 003-- Custom NSOperation[14013:11296147] Contracted Dog 2021-05-26 10:59:07.352682+0800 003-- Custom NSOperation[14013:11296171] 2021-05-26 10:59:07.371843+0800 003-- Custom NSOperation[14013:11296141] 2021-05-26 10:59:07.789925+0800 003-- Custom NSOperation[14013:11296143] 2021-05-26 10:59:07.807064+0800 003-- Custom NSOperation[14013:11296166] download picture: Custom NSOperation[14013:11296170] 2021-05-26 10:59:07.840837+0800 003-- Custom NSOperation[14013:11296172] download image: 2021-05-26 10:59:08.311868+0800 003-- Custom NSOperation[14013:11296154] download picture: Contracted Dog 2021-05-26 10:59:08.328546+0800 003-- Custom NSOperation[14013:11296172] Contracted Dog 2021-05-26 10:59:08.345750+0800 003-- Custom NSOperation[14013:11296144] 2021-05-26 10:59:08.362655+0800 003-- Custom NSOperation[14013:11296167] download picture: Cute SupermanCopy the code

Repeat download, for example: Download picture: Elegant church executed multiple times, wasting memory.

The solution

3.1 Loading data from the model

If (model.image) {NSLog(@" load data from model "); cell.imageView.image = model.image; return cell; } [[NSOperationQueue mainQueue] addOperationWithBlock:^{ cell.imageView.image = image; model.image = image; }]; 2021-05-26 11:01:30.524417+0800 003-- Custom NSOperation[14094:11306727] 2021-05-26 11:01:30.525197+0800 003-- Custom NSOperation[14094:11306723] 2021-05-26 11:01:30.525595+0800 003-- Custom NSOperation[14094:11306728] 2021-05-26 11:01:30.526080+0800 003-- Custom NSOperation[14094:11306725] Wallpapers 2021-05-26 11:01:30.526448+0800 003-- Custom NSOperation[14094:11306722] 2021-05-26 11:01:30.526841+0800 003-- Custom NSOperation[14094:11306724] 2021-05-26 11:01:30.527250+0800 003-- Custom NSOperation[14094:11306731] 2021-05-26 11:01:30.527632+0800 003-- Custom NSOperation[14094:11306733] 2021-05-26 11:01:30.669017+0800 003-- Custom NSOperation[14094:11306738] [] Nw_PROTOCOL_GET_quic_IMAGe_BLOCK_invoke Dlopen libquic failed 2021-05-26 11:03:40.483158+0800 003-- NSOperation[14094:11306728] 2021-05-26 11:03:40.498987+0800 003-- Custom NSOperation[14094:11315435] 2021-05-26 11:03:40.515254+0800 003-- Custom NSOperation[14094:11315433] 2021-05-26 11:03:40.531969+0800 003-- Custom NSOperation[14094:11315441] 2021-05-26 11:03:40.561970+0800 003-- Custom NSOperation[14094:11315434] 2021-05-26 11:03:40.578452+0800 003-- Custom NSOperation[14094:11315445] 2021-05-26 11:03:40.612491+0800 003-- Custom NSOperation[14094:11315440] Contracted Dog 2021-05-26 11:03:40.629275+0800 003-- Custom NSOperation[14094:11315447] Contracted Dog 2021-05-26 11:03:40.661915+0800 003-- Custom NSOperation[14094:11315446] Contracted Dog 2021-05-26 11:03:40.678988+0800 003-- Custom NSOperation[14094:11315444] Contracted Dog 2021-05-26 11:03:40.728374+0800 003-- Custom NSOperation[14094:11315450] 2021-05-26 11:03:40.744980+0800 003-- Custom NSOperation[14094:11315449] 2021-05-26 11:03:41.299598+0800 003-- Custom NSOperation[14094:11306166] Load data from model 2021-05-26 11:03:41.315792+0800 003-- Customize NSOperation[14094:11306166] Load data from model 2021-05-26 11:03:41.331618+0800 003-- Customize NSOperation[14094:11306166] Load data from model 2021-05-26 11:03:41.348666+0800 003-- Customize NSOperation[14094:11306166] Load data from model 2021-05-26 11:03:42.868586+0800 003-- Customize NSOperation[14094:11306166] Load data from model 2021-05-26 11:03:42.885300+0800 003-- Customize NSOperation[14094:11306166] 2021-05-26 11:03:42.901959+0800 003-- Custom NSOperation[14094:11315445] 2021-05-26 11:03:42.918589+0800 003-- Custom NSOperation[14094:11306728] 2021-05-26 11:03:42.951874+0800 003-- Custom NSOperation[14094:11315444] 2021-05-26 11:03:42.968557+0800 003-- Custom NSOperation[14094:11306166] loads data from the modelCopy the code

Loading data from model – there is a small bug: sliding the page after loadingLoad data from the modelAnd thenClick simulator.Select Debug on the menu bar Simulate Memory WarningAfter that, the memory warning function is calleddidReceiveMemoryWarning

- (void)didReceiveMemoryWarning{NSLog(@" Memory warning, you need to clean up memory!!") ); }Copy the code
2021-05-26 11:12:10.189135+0800 003-- Custom NSOperation[14535:11382577] Load data from model 2021-05-26 11:12:10.204499+0800 2021-05-26 11:12:10.466714+0800 003-- Custom NSOperation[14535:11383187] 2021-05-26 11:12:10.483011+0800 003-- Custom NSOperation[14535:11383199] 2021-05-26 11:12:13.340177+0800 003-- Custom NSOperation[14535:11382577] received a memory warning, you need to clean up memory!!Copy the code

At this point, it is not advisable to delete the data in the model, because every time the memory is deleted, the memory is consumed and the power of the phone is greatly affected.

The correct way to handle this is as follows:

3.2 Loading Data from the Memory or disk

Memory and disk issues: Memory is stored in dictionaries, arrays and other OC or C objects, linked list, the corresponding memory will be deleted after the page exit or program exit, 'generally read by key' disk is the sandbox stored in the plist,file and other files, read much slower than memory. 'Read through path' // memory, disk first load memory (fast) --> disk (save a copy to memory) --> download save memory and diskCopy the code
UIImage *cacheImage = self.imagecachedict [model.imageurl]; If (cacheImage) {NSLog(@" load data from memory "); cell.imageView.image = cacheImage; return cell; } cacheImage = [UIImage imageWithContentsOfFile:[model.imageurl getDowloadImagePath]]; If (cacheImage) {NSLog(@" load data from disk "); cell.imageView.image = cacheImage; [self.imagecachedict setObject:cacheImage forKey: model.imageurl]; return cell; } /** @return MD5 encryption of the image download address */ - (NSString *)getDowloadImagePath{// url --> unique, the same url, according to the path is not unique, Md5 ensures that the unique // URL is long; Md5 can ensure that the length of the fixed nsstrings * cachePath = [NSSearchPathForDirectoriesInDomains (NSCachesDirectory NSUserDomainMask, YES) lastObject]; NSString *md5Str = [self kc_md5String]; return [cachePath stringByAppendingPathComponent:md5Str]; } /** MD5 encryption @return return MD5 encryption data */ - (NSString *)kc_md5String{// const char *cStr = [self UTF8String]; // unsigned char result[16]; // CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // return [NSString stringWithFormat: // @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", // result[0], result[1], result[2], result[3], // result[4], result[5], result[6], result[7], // result[8], result[9], result[10], result[11], // result[12], result[13], result[14], result[15] // ]; const char *str = self.UTF8String; if (str == NULL) { str = ""; } unsigned char r[CC_MD5_DIGEST_LENGTH]; CC_MD5(str, (CC_LONG)strlen(str), r); NSURL *keyURL = [NSURL URLWithString:self]; NSString *ext = keyURL ? keyURL.pathExtension : self.pathExtension; return [NSString stringWithFormat: @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%@", r[0], r[1], r[2], r[3], r[4], r[5], r[6], r[7], r[8], r[9], r[10],r[11], r[12], r[13], r[14], r[15], ext.length == 0 ? @"" : [NSString stringWithFormat:@".%@", ext]]; }Copy the code

When storing data, make sure the current address is unique.

/Users/hai/Library/Developer/CoreSimulator/Devices/DFFAFFED-D649-4A68-9DC5-5CC46BEFBB37/data/Containers/Data/Application /ED0A679B-1B7B-4E5C-B733-55795509F611Copy the code

http://e.hiphotos.baidu.com/image/h%3D300/sign=0708e32319ce36d3bd0485300af23a24/fcfaaf51f3deb48fd0e9be27fc1f3a292cf57842 .jpg md5Str=== e69dc1165d81d02165c1c19efe6102f8.jpgCopy the code
http://e.hiphotos.baidu.com/image/h%3D300/sign=0708e32319ce36d3bd0485300af23a24/fcfaaf51f3deb48fd0e9be27fc1f3a292cf57842 .jpg md5Str=== e69dc1165d81d02165c1c19efe6102f8.jpgCopy the code

The above statement is wrong. General third party network framework, the network requests, executes nsstrings. * URLIdentifier = request URL. AbsoluteString, which ignored the domain name server, will only perform image download address relative URL address, if change the server domain name, relative address did not change, If md5 is used directly, it is md5 of absolute address, which ensures the uniqueness of address.

https://www.baidu.com/photos/1.jpg https://www.sina.com/photos/1.jpg request.URL.absoluteString = photos/1.jpg Request. URL. AbsoluteString = photos / 1. JPG / / after using md5 absoluteString ([URL + md5 (URL)]) = photos / 1. JPG + md5(https://www.baidu.com/photos/1.jpg) absoluteString([URL+md5(URL)]) = photos/2.jpg + md5(https://www.sina.com/photos/1.jpg)`Copy the code

This depends on the key stored in the cache cache.db file. The above explanation may be different, but the principle is similar. This is also the benefit of using a third party to do a lot of extra work.

3.3 Cache Download Tasks

3.2 There is a bug in reading from memory and disk.

Here’s how it works: Open up the simulator, open up Charles, remember toCheck the MacOS ProxySet the bandwidth, as shown in the following figureIn a poor network speed or low-end computer, the image has not been downloaded back or writing to disk is very slow, keep adding tasks on the queue, resulting in memory surge, a certain value, the program will flash back. The network delay is 60 seconds by default. If the request is not finished within 60 seconds, the page will keep sliding to add tasks.

The solution is to perform the same operation only once and do not perform the download task again.

If (self.operationDict[model.imageurl]) {NSLog(@" brother, wait a moment < %@ has submitted for download ",model.title); return cell; }Copy the code
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{// repeated task}]; // Add the downloaded transaction to the queue [self.queue addOperation:op]; // Record the download transaction [self.operationDict setObject:op forKey: model.imageurl];Copy the code
/ / the download is complete Cleared from record, or if the download is complete but download only half of it, wouldn't be downloaded again [self. OperationDict removeObjectForKey: model. The imageUrl];Copy the code

4. Customize NSOperation

The process in 3 is very complicated. So much code is needed to execute a picture download page every time. The customized NSOperation can be encapsulated here. Reference SdWebImage

[cell.imageView sd_setImageWithURL:[NSURL URLWithString:model.imageUrl]];
Copy the code

Demo address: github.com/tanghaitao/…

A single principle: ImageView classification, scheduling ImageView Manager, download NSOperation

1. The UIImageView + KCWebCache classification [cell imageView kc_setImageWithUrlString: model. The imageUrl title: model. The title indexPath:indexPath]; 2.KCWebImageManager singleton manages all cache policies, maximum concurrency, Memory warning // as long as I call simple interest, it will come here so I can do a bunch of initializations here - (instanceType)init{if (self=[super init]) { self.queue.maxConcurrentOperationCount = 2; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(memoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; } return self; } 3. KCWebImageDownloadOperation custom Operation image download tasks, Callback execution results https://developer.apple.com/library/archive/navigation/ search NSOperation find Sample For non - concurrent operations, you typically override only one method - main If you are creating a concurrent operation, you need to override the following methods and properties at a minimum: -start-asynchronous - finished: executing a start function is required for a non-concurrent mode. If you want setters when you overload you have to synthesize them manually.Copy the code
- (void)start{// Extend the life cycle // large loop // create your own management thread @autoreleasepool{[_lock lock]; // Thread safety [_lock unlock]; }}Copy the code

5. User-defined summary of NSOperation

// Do you need to create an image for the same url? If (self.operationdict [urlString]) {NSLog(@" downloading, let the bullet fly %@",title); NSLog(@" downloading callback Block %@'s %@",title,completeHandle); NSMutableArray *mArray = self.handleDict[urlString]; if (mArray == nil) { mArray = [NSMutableArray arrayWithCapacity:1]; } [mArray addObject:completeHandle]; // The first task at the same URL starts with 1 array completeHandle1 // The second task at the same URL starts with 2 arrays completeHandle1, completeHandle2 [self.handleDict setObject:mArray forKey:urlString]; return; } / / here's a create operation download - custom KCWebImageDownloadOperation * downOp = [[KCWebImageDownloadOperation alloc] initWithDownloadImageUrl:urlString completeHandle:^(NSData *imageData,NSString *kc_urlString) { UIImage *downloadImage =  [UIImage imageWithData:imageData]; if (downloadImage) { [self.imageCacheDict setObject:downloadImage forKey:urlString];  [self.operationDict removeObjectForKey:urlString]; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ CompleteHandle (downloadImage,kc_urlString); // Get the result of all tasks calling // the same URL if (self.handleDict[kc_urlString]) {NSMutableArray *mArray = self.handleDict[kc_urlString]; for (KCCompleteHandleBlock handleBlock in mArray) { handleBlock(downloadImage,kc_urlString); } [self.handleDict removeObjectForKey:urlString]; } }]; } } title:title];Copy the code
[mArray addObject:completeHandle];
[self.handleDict setObject:mArray forKey:urlString];
Copy the code

If you have multiple tasks of the same URL on the interface, and one of the tasks of the same URL completes, you don't need to create a new download task.

if (self.kc_urlString && self.kc_urlString.length>0 && ! [the self kc_urlString isEqualToString: urlString]) {/ / download ing not back (picture is too big Speed too slow) cancel (dead), // NSLog(@" cancel previous download operation %@",title); NSLog (@ "cancel % @ before download operation: % @ - % @ \ n % @ - % @", indexPath, self. Kc_title, title, and the self. The kc_urlString, urlString); [[KCWebImageManager sharedManager] cancelDownloadImageWithUrlString:self.kc_urlString]; } self.kc_urlString = urlString; self.kc_title = title; self.image = nil; [[KCWebImageManager sharedManager] downloadImageWithUrlString:urlString completeHandle:^(UIImage *downloadImage,NSString *urlString) {// If ([urlString isEqualToString:self.kc_urlString]) {self.kc_urlString = nil; self.kc_title = self nil; self.image = downloadImage; } } title:title]; - (void)setKc_urlString (NSString *)kc_urlString{/** 1: bound object 2: associated key 3: value 4: */ objc_setAssociatedObject(self, kcAssociatedKey_imageUrlString, kc_urlString, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)kc_urlString{ return objc_getAssociatedObject(self, kcAssociatedKey_imageUrlString); }Copy the code

The runtime objc_setAssociatedObject, objc_getAssociatedObject listens for changes in the current task property kc_urlString. If no request is completed, Download kc_urlString has not said is nil ing no back (the picture is too big Speed too slow), need to cancel this task (dead) cancelDownloadImageWithUrlString: self. Kc_urlString