官术网_书友最值得收藏!

  • Swift High Performance
  • Kostiantyn Koval
  • 702字
  • 2021-08-05 16:36:25

Immutability

In the previous section, you learned how important it is to use immutable constants. There are more immutable types in Swift, and you should take advantage of them and use them. The advantages of immutability are as follows:

  • It removes a bunch of issues related to unintentional value changes
  • It is a safe multithreading access
  • It makes reasoning about code easier
  • There is an improvement in performance

By making types immutable, you add an extra level of security. You deny access to mutating an instance. In our journal app, it's not possible to change a person's name after an instance has been created. If, by accident, someone decides to assign a new value to the person's firstName, the compiler will show an error:

var person = Person(firstName: "Jon", lastName: "Bosh")
p.firstName = "Sam" // Error

However, there are situations when we need to update a variable. An example could be an array; suppose you need to add a new item to it. In our example, maybe the person wants to change a nickname in the app. There are two ways to do this, as follows:

  • Mutating an existing instance
  • Creating a new instance with updated information

Mutating an instance in place could lead to a dangerous, unpredictable effect, especially when you are mutating a reference instance type.

Note

Classes are reference types. "Reference type" means that many variables and constants can refer to the same instance data. Changes done to the instance data reflect in all variables.

Creating a new instance is a much safer operation. It doesn't have any impact on the existing instances in the system. After we have created a new instance, it may be necessary to notify other parts of the system about this change. This is a safer way of updating instance data. Let's look at how we can implement a nickname change in our Person class. First, let's add a nickname to the Person class:

class Person {
  let nickName: String
…

func changeNickName(nickName: String) -> Person  {
    return Person(firstName: firstName, lastName: lastName,
                   nickName: nickName)
  }
}

let sam = Person(firstName: "Sam", lastName: "Bosh", 
  nickName:"sam")
let rockky = sam.changeNickName("Rockky")

Because we made a sam instance a constant, we can't assign a new value to it after changing nickName. In this example, it would be better to make it a variable because we actually need to update it:

var sam = Person(firstName: "Sam", lastName: "Bosh", 
  nickName:"BigSam")
sam = sam.changeNickName("Rockky")

Multithreading

We get more and more core processors nowadays, and working with multithreading is a part of our life. We have GCD and NSOperation for performing work on multiple threads.

The main issue with multithreading is synchronizing read-and-write access to data without corrupting that data. As an example, let's create an array of journal entries and try to modify it in the background and main thread. This will lead to an application crash:

class DangerousWorker {
  var entries: [JournalEntry]

  init() {
    //Add test entries
    let entry = JournalEntry(title: "Walking", text: "I was 
      walking in the loop")
    entries = Array(count: 100, repeatedValue: entry)
  }

  func dangerousMultithreading() {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
      sleep(1) //emulate work
      self.entries.removeAll()
    }
  
    print("Start Main")
    for _ in 0..<entries.endIndex {
      entries.removeLast() //Crash
      sleep(1) //emulate work
    }
  }
}

let worker = DangerousWorker()
worker.dangerousMultithreading()

These kinds of issues are really hard to find and debug. If you remove the sleep(1) delay, the crash might not occur on some devices, depending on which thread is run first.

When you make your data immutable, it becomes read-only and all threads can read it simultaneously without any problems:

let entries: [JournalEntry]

let entry = JournalEntry(title: "Walking", text: "I was walking")
entries = Array(count: 100, repeatedValue: entry)
// entries is immutable now, read-only

dispatch_async(dispatch_get_global_queue(
              DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) {
 
  for entry in self.entries {
    print("\(entry) in BG")
  }
}

for entry in self.entries {
  print("\(entry) in BG")
}

But we often need to make changes to the data. Instead of making changes directly to the source data, a better solution is to create new, updated data and pass the result to the caller thread. In this way, multiple threads can safely continue performing a read operation. We will take a look at multithreading data synchronization in Chapter 6, Architecting Applications for High Performance.

主站蜘蛛池模板: 炎陵县| 泊头市| 淄博市| 阿拉尔市| 隆化县| 佳木斯市| 宁国市| 瑞金市| 武川县| 开鲁县| 米泉市| 青龙| 长子县| 安国市| 江达县| 柳江县| 固始县| 土默特右旗| 武清区| 类乌齐县| 府谷县| 阳谷县| 竹山县| 伊宁市| 临朐县| 天峨县| 嵊州市| 云阳县| 张北县| 鹿邑县| 大姚县| 和林格尔县| 益阳市| 德钦县| 肃北| 阜宁县| 大余县| 衡山县| 大荔县| 灵台县| 龙泉市|