12/08/2018, 15:22

iOS Concurrency - Phần 3.3: Grand Central Dispatch

Vấn đề liên quan đến Singletons và giải pháp Singleton là một trong những design pattern phổ biến lập trình nói chung và iOS nói riêng. Singleton đảm bảo chỉ có một instance của một class được tạo ra và được truy cập từ bất cứ nơi đâu. Một vấn đề chúng ta hay gặp với Singletons là nó không ...

Vấn đề liên quan đến Singletons và giải pháp

Singleton là một trong những design pattern phổ biến lập trình nói chung và iOS nói riêng. Singleton đảm bảo chỉ có một instance của một class được tạo ra và được truy cập từ bất cứ nơi đâu. Một vấn đề chúng ta hay gặp với Singletons là nó không an toàn về luồng (thread safe). Đôi khi chúng ta gặp phải trường hợp nhiều controller truy xuất đến singleton instance cùng một lúc. Tại đây chúng ta lại gặp bài toán nhiều thread cùng truy xuất tới tài nguyên dùng chung tại một thời điểm dẫn tới race condition. Race condition làm cho dữ liệu chúng ta không chính xác hoặc làm crash app. Do đó khi hiện thực Singleton chúng ta phải đảm bảo rằng nó chúng phải thật sự an toàn về luồng. Với Singleton có hai trường hợp cần xém xét để đảm bảo an toàn về luồng: thứ nhất là trong quá trình khởi tạo singleton instance, thứ 2 là trong quá trình đọc và viết tới instance.

  1. Việc khởi tạo single instance là trường hợp dễ dàng bởi vì cách Swift khởi tạo biến toàn cục (global variable). Biến toàn cục được khởi tạo khi lần đầu được truy cập và chúng được đảm bảo chỉ khởi tạo một lần. Phần code khởi tạo được đối xử như là vùng găng (critical section) và được đảm bảo hoàn thành trước khi luồng khác truy cập tới biến toàn cục. Mình đã đề cập tới khái niệm critical section ở bài trước, các bạn có thể xem lại. Dưới đây là cách singleton được khởi tạo.
private let _sharedManager = BookManager()
class BookManager {
  class var sharedManager: BookManager {
    return _sharedManager
  }
}

Biến toàn cục private _ sharedManager được sử dụng để khởi tạo BookManager lazily. Biến public shareManager trả về biến private _ sharedManager. Swift đảm bảo rằng hoạt động này an toàn về luồng. Bạn vẫn còn phải giải quyết với vấn đề an toàn về luồng khi truy cập đoạn code trong singleton mà nó thao tác (đọc viết) trên dữ liệu dùng chung. Bạn có thể giải quyết vấn đề này thông qua qua việc truy cập dữ liệu tuần sự (synchronizing data access) mà chúng ta sẽ đề cập ngay sau đây 2. Trường hợp thứ 2 chúng ta cần xem xét là khi chúng ta đọc, viết tới instance Trong Swift, bất cứ một biến nào được khai báo với từ khoá let được xem như là hằng (constant) và chỉ có thể đọc (readonly) do đó an toàn về luồng. Việc khái báo một biến với từ khoá var giúp chúng ta chỉnh sửa được (mutable) nhưng lại không an toàn về luồng nếu kiểu dữ liệu không được thiết kế để an toàn về luồng. Những kiểu collection trong Swift như Array và Dictionary thì không an toàn về luồng khi khai báo với từ khoá var. Mặc dù nhiều thread có thể đọc một instance của một Array được khai báo với từ khoá var đồng thời mà không có vấn đề gì, tuy nhiên sẽ không an toàn nếu một thread sửa Array trong khi thread khác đọc nó. Singleton khai báo bên trên không ngăn được trường hợp này. Chúng ta cùng xem đoạn code sau đây

private let _sharedManager = BookManager()
class BookManager {
  class var sharedManager: BookManager {
    return _sharedManager
  }
  
  fileprivate var _books: [Book] = []
  var books: [Book] {
    return _books
  }
  
  func addBook(_ book: Book) {
    _books.append(book)
    DispatchQueue.main.async {
      self.postContentAddedNotification()
    }
  }
  
  fileprivate func postContentAddedNotification() {
    NotificationCenter.default.post(name: Notification.Name(rawValue: "Add book successful"), object: nil)
  }
}

Hàm addBook(_            </div>
            
            <div class=

0