NSOperation

  • Is aAn abstract class(The NSOperation class is an abstract class)

    Cannot be used directly because only the method declaration does not implement subclasses that can use it: NSIvocationOperation, NSBlockOperation(more common)

NSOperationQueue

  • Queues (default concurrency)
  • Core: Adds operations to queues
  • Common properties & methods
    • Maximum concurrency (maximum number of threads the queue can operate on)

    queue.maxConcurrentOperationCount = 2;

    • Queue pause & Continue (Tasks can be added to the queue after a pause, but will not be executed)

    queue.suspended = YES;

    • Cancel all operations

    [queue cancelAllOperations];

NSInvocationOperation

  • IOS2.0 launched
  • NSTread based encapsulation
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [selfdemo2]; } - (void)demo2{
    NSInvocationOperation *io = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@0];
    // Start is not added to the queue, but is performed on the current thread[io start]; } - (void)demo1{
    1. Create a queue
    NSOperationQueue is a concurrent queue
    NSOperationQueue *queue = [NSOperationQueue new];
    
    for (int i = 0; i<5; i++) {
        //2. Create a vm
        //NSInvocationOperation defaults to asynchronous operations (tasks)
        NSInvocationOperation *io = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo:) object:@(i)];
        //3. Add the operation to the queue
        [queue addOperation:io];
    }
}

-(void)demo:(NSNumber *)num{
    NSLog(@ % @ % @ "",num,[NSThread currentThread]);
}
Copy the code

In Demo2, the start method is called and the operation is executed in the current thread

NSBlockOperation

  • IOS4.0 launched
  • GCD based package
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    [selfdemo2]; } - (void)demo3{
    NSBlockOperation *bk = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@ "% @"[NSThread currentThread]);
    }];
    
    [bk start];
}

// The object cannot be fetched
// Thread communication- (void)demo2{
    // Asynchronous operation, concurrent queue
    [_queue addOperationWithBlock:^{//Block is an operation executed in the same thread
        for (int i=0; i<5; i++) {
            NSLog(@" Pretend to download %d... % @",i,[NSThread currentThread]);
        }
        
        NSString *str = @ "123";
        // The main queue is updated
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            NSLog(@" Pretend to update %@... % @",str,[NSThread currentThread]);
        }];
    }];
}

// The operation object can be fetched- (void)demo1{
    // Asynchronous operation
    NSBlockOperation *bk1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@ "% @"[NSThread currentThread]);
        NSLog(@ "% @"[NSThread currentThread]);
    }];
    
    NSBlockOperation *bk2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@ "% @"[NSThread currentThread]);
    }];
    
    // Parallel queue
    [_queue addOperation:bk1];
    [_queue addOperation:bk2];
}
Copy the code
  • Common properties & methods
    • The quality of service

    op1.qualityOfService = NSQualityOfServiceUserInteractive;

    • priority

    op1.queuePriority = NSOperationQueuePriorityVeryHigh;

    • Add the dependent

    [op2 addDependency:op1];

    • Cancel the rely on

    [op2 removeDependency:op1];

Asynchronous image download + cache management

  • Key points:
    • Child thread downloads image, main thread updates UI
    • Memory Cache Pool (NSMutableDictionary)
    • Operation cache pool (removed before completion)
    • The operation completes just after the cell is reused, causing error refresh problems (reloadRows SATIndexPaths)
    • Sandbox cache (1. Determine after memory cache 2. Write to memory cache after sandbox cache)
    • Memory warning handling (clear image & operation cache pool)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    MYTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"mycell" forIndexPath:indexPath];
    MYAppInfo *model = _appList[indexPath.row];
    cell.titleLbl.text = model.name;
    cell.detailLbl.text = model.download;
    
    //1. Check whether there are cached images in the memory. If so, load them directly to improve loading efficiency and reduce network usage
    if (_ramImg[model.icon]) {
        NSLog(@load from memory);
        cell.iconView.image = _ramImg[model.icon];
        return cell;
    }
    
    UIImage *cellImg = [UIImage imageWithContentsOfFile:[model.icon cachesPath]];
    //2. Check whether there are cached images in the sandbox, or load them directly from the sandbox
    if (cellImg) {
        NSLog(@" Load from sandbox");
        cell.iconView.image = cellImg;
        [_ramImg setObject:cellImg forKey:model.icon];
        return cell;
    }
    
    // Start the program, there is no memory and sandbox cache, first give a placeholder map, to avoid the picture even if the load is good, but the picture layout is not refreshed, the picture has no size can not display the problem
    cell.iconView.image = [UIImage imageNamed:@"user_default"];
    
	//3. Check whether an operation is being performed on the picture of the current cell. If no operation is being created, the operation is not continued
    if (_opList[model.icon]) {
        return cell;
    }
	//4. Create operation, download image from network
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
		NSLog(@" Load from network");
		if (indexPath.row > 11) {
			// There are two problems with rolling back and forth when simulating network request data delay
			//1. When the delay is long, the scroll tabelView will continue to enter the data source method, while the image data is not cached in memory, it will continue to enter the create method, and repeat the create operation
			//2. The problem of cell reuse is that the previous operations after cell reuse are not updated to the previous cell in time due to network delay, and the previous cell is pushed out for later reuse. At this time, the previous data is just loaded, and the reusing operation will refresh the current cell
			[NSThread sleepForTimeInterval:5];
		}
		NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:model.icon]];
        UIImage *iconImg = [UIImage imageWithData:data];
        //6. Download complete, thread communication, return to main thread
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{ 
// cell.iconView.image = iconImg;
			if (iconImg) {
				//7. Write the loaded image to the memory cache pool to avoid the need to re-load the image to the network
				[_ramImg setObject:iconImg forKey:model.icon];
				// Let the operation refresh only the row data when the operation was created. Solve the network latency problem 2
				[self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation: UITableViewRowAnimationNone];
				//8. Write data to the sandbox
				[data writeToFile:[model.icon cachesPath] atomically:YES];
			}
			After the operation is complete, the operation will be removed from the cache pool, regardless of whether the image network request is successful or not, so it should be put after the judgment. If not removed, the image that has not been successfully requested will not be able to enter the operation again
			[_opList removeObjectForKey:model.icon];
		}];
    }];
    //5. Add the operation to the operation cache pool and record the completed operation
    [_opList setObject:op forKey:model.icon];
    [_queue addOperation:op];
    
    return cell;
}

// Resolve the memory warning and release the memory cache
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    
    [_ramImg removeAllObjects];
    [_opList removeAllObjects];
}
Copy the code