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

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.

主站蜘蛛池模板: 湛江市| 长春市| 彭山县| 石景山区| 黎城县| 蓝山县| 菏泽市| 怀柔区| 新丰县| 怀安县| 襄城县| 三穗县| 临海市| 大理市| 乌拉特前旗| 阿瓦提县| 武城县| 中西区| 房产| 横峰县| 甘泉县| 海晏县| 涟源市| 濮阳市| 海口市| 永仁县| 延寿县| 东海县| 丽水市| 肥西县| 岢岚县| 环江| 都江堰市| 板桥市| 桃源县| 虞城县| 凌源市| 高雄市| 沂源县| 阿拉善盟| 秦安县|