The article now basically becomes week more, actually is very hard. / (ㄒ o ㄒ) / ~ ~

In fact, I don’t want to, I also want to learn those studious children, more nimble day. But I can’t do it. It’s so hard. In this article or after fighting countless procrastination, gave himself a death order, psychological thought: “today must update, must update!!”

In this way, three days after the deadline I set for myself, I finally wrote the example.

We shared some of the basics of Operation, implemented the basic multithreading example, and learned how to set priorities.

Today’s starter is a Demo of how to set up a dependency. Operation is used to load images on the CollectionView. This is an example that happens all the time in production. Load the interface first, and then slowly load the image into the Item.

This example is intentionally a little more complicated, just to learn something new.

Tap the blackboard, tap the blackboard, tap the blackboard. Custom Operation subclasses, map functions, new tuple data types for Swift.

The following is an example of a CollectionView asynchronously loading an image:

1. Operation Sets the dependency relationship

From the ground up, we’re going to start today’s pre-dinner snack. Let’s start by looking at how to set the operation dependency.

What is dependency? What’s the point?

For example, we want to make a paid App project for listening to music, which needs to go through four steps: login, payment, download and play. In fact, it is obvious that the four operations are sequential, but if all the operations are multithreaded, how can we control the order? By setting priority? NO! Priorities can’t do this. If you think setting priorities can be done, go back to my last article. Swift multithreaded Operation: load images by priority

We can establish a sequence of priorities by setting up dependencies. An operation can be executed only when all operations on which it depends have been executed.

Operation can also establish dependencies across queues. Operation can be dependent across queues. Operation can be dependent across queues. Three times.

A small note is that the operation dependency must be established before adding to the queue.

Let’s go back to the template we used last time. Oh, oh, don’t force me to write a new template, to pay attention to reuse. Actually, I’m too lazy to write a new one.

See yet? Images are loaded from top to bottom instead of being displayed in a chaotic order.

The code is very simple, look at this:

fileprivate func startDepencyDemo() {
   operationQueue.maxConcurrentOperationCount = 4
    self.activityIndicator.startAnimating()
    guard let url = URL(string: "https://placebeard.it/355/140") else {return }
    let op1 = convenienceOperation(setImageView: imageView1, withURL: url)
    let op2 = convenienceOperation(setImageView: imageView2, withURL: url)
    op2.addDependency(op1)
    let op3 = convenienceOperation(setImageView: imageView3, withURL: url)
    op3.addDependency(op2)
    let op4 = convenienceOperation(setImageView: imageView4, withURL: url)
    op4.addDependency(op3)
    
    DispatchQueue.global().async {
        [weak self] inself? .operationQueue.addOperations([op1,op2,op3,op4],waitUntilFinished: true) DispatchQueue.main.async { self? .activityIndicator.stopAnimating() } } }Copy the code

Ok, so let’s see how we can write that asynchronously loading CollectionView image. But before we do that, we need a little bit of pre-knowledge.

2. Preloading knowledge content

2.1 Customizing Operation subclasses

  • You cannot cancel the Finished operation state.
  • IsFinished is set to if the operation succeeds, fails, or is canceledtrue. So don’t rely on this attribute to determine if it was successfully executed.

2.1.1 What needs to be rewritten

Creating a concurrent subclass of Operation can be a bit more cumbersome. By default, subclasses of Operation execute synchronously, and if we were to create a subclass capable of concurrency, we might need to override some methods.

  1. Start: All parallel Operations must override this method and then call it manually in the thread they want to execute. Note: The start method of the parent class cannot be called at any time.

  2. Main: Optional. Although we can execute the task in the start method, using main to set the code to execute the task makes the operation structure clearer.

  3. IsExecuting: Required. Whether the command is being executed. , the KVO notification mechanism needs to be implemented.

  4. IsFinished: Required. Whether it is completed. , the KVO notification mechanism needs to be implemented.

  5. IsAsynchronous: required. This method returns false by default, indicating non-concurrent execution. Concurrent execution requires customization and returns true.

2.1.2 Code implementation

fileprivate var _executing : Bool = false

override var isExecuting: Bool {
    get { return _executing }
    set {
        ifnewValue ! = _executing { willChangeValue(forKey: "isExecuting")
            _executing = newValue
            didChangeValue(forKey: "isExecuting")
        }
    }
}

fileprivate var _finished : Bool = false
override var isFinished: Bool {
    get { return _finished }
    set {
        ifnewValue ! = _finished { willChangeValue(forKey: "isFinished")
            _finished = newValue
            didChangeValue(forKey: "isFinished")
        }
    }
}



override var isAsynchronous: Bool {
    get {
        return true
    }
}

override func start() {
    if! isCancelled { isExecuting =true
        isFinished = false
        startOperation()
    } else {
        isFinished = true
    }
Copy the code

You are probably wondering why you need to define _executing and _finished.

See? I only give get methods, not set methods. So there’s no way to use this property directly.

2.1.3 Description of canceling operations

Operation doesn’t just mean setting isCancelled. The value isCancelled should be checked regularly while writing code. If true, we should stop executing the next task immediately.

2.2 the map function

What is map for? Let me give you an example. It’ll make it easier to understand.

Take a look at the code:

let testNumberArray = [1,2,3,4,5,6,7,8,9]
print("Print result before using map :\(testNumberArray)")

let newArray = testNumberArray.map{$0 + 2}
print("NewArray Print Result :\(newArray)")


let stringArray = testNumberArray.map { (number) -> String in
  return "No.\(number.description)"
}
print("StringArray print result :\(stringArray)")
Copy the code

A little confused, isn’t it? Never mind, let’s see what the print looks like:

Is it amazing? One array, very simply becomes two arrays.

Swift is a language that supports functional programming, and Map is an operation for collection types. The map method iterates through the caller, performing the operations defined in the closure on each element in the array.

So what newArray does is it increments every element in the testNumberArray array by 2.

StringArray performs the operation of turning each element in the testNumberArray array into a string preceded by “No.”

What’s the fxxk! Are you sharp? Are you sharp? There are FlatMap,Filter,Reduce operation for collection, interested children’s shoes please study by yourself.

2.3 Tuple Data type added to Swift

A tuple is actually a compound value. Simply put, multiple values are combined into a compound value using parentheses. Values within a tuple can be of any type, and tuples do not require values within a tuple to be of the same type.

let (day, content) = (30,"It's a nice day.")
Copy the code

This is the simplest tuple definition.

This is a little more advanced.

        let week = (name : "Monday", order: 1) //let weekName = week.name
        let weekOrder = week.order
Copy the code

Tuples are not what we’re going to focus on today, but we just used them in the demo, so we’re going to mention them a little bit.

Tuples can judge complex conditions with Switch marquee. Can be used as the return value of a method to return multiple values; They can pretend to be structures;

3. Images in CollectionView are loaded asynchronously

Take a look at the mind map:

You can download the source code to watch, only the Swift version of the download.

The code uses Swift’s unique data format, which of course does not provide objective-C source code. And the title of the article is “Swift Multi-threaded Operation”. Please forgive me for adding the ID of an atypical tech nerd in the article. It is some children’s shoes in the reprint of the time completely not signed, ╮(╯▽╰)╭ ah

The code for assigning the key points of the image to item:

override func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    let cell = cell as! CollectionCell
    let(item, operation) = imageOps[indexPath. Row] // Just in case, we stop operation? .cancel() weak var weakCell = celllet newOp = ImageLoadOperation(forItem: item) { (image) inDispatchQueue.main.async { weakCell? .imageView.image = image } } imageLoadQueu.addOperation(newOp) imageOps[indexPath.row] = (item, newOp) }Copy the code

Thank you for watching, and that’s the end of today’s news broadcast.

Rich big ye click on the bottom of the point to sell laugh money, strength on github to give a star ✨. Or let’s chat and brag in the comments. hiahia~

Oh, give a warning. According to the previous plan, the next article should be GCD basics. O~M~G, so boring.

  • Finally, all the code is here: download it from gitHub and give it a Star ~(~ o ~ 3 ~) ~ love you