20/07/2019, 09:48

Lazy var trong iOS Swift

Trong khi phát triển ứng dụng iOS, chắc chắn rằng các dev quan tâm rất nhiều về dung lượng bộ nhớ mà ứng dụng sử dụng. Nếu ứng dụng là một ứng dụng phức tạp, thì vấn đề bộ nhớ sẽ là một thách thức lớn đối với các dev. Vì vậy, các dev của chúng ta phải rất cẩn thận khi viết code và đương nhiên ...

Trong khi phát triển ứng dụng iOS, chắc chắn rằng các dev quan tâm rất nhiều về dung lượng bộ nhớ mà ứng dụng sử dụng. Nếu ứng dụng là một ứng dụng phức tạp, thì vấn đề bộ nhớ sẽ là một thách thức lớn đối với các dev. 

Vì vậy, các dev của chúng ta phải rất cẩn thận khi viết code và đương nhiên đặt ưu tiên hàng đầu cho việc tối ưu hoá cấp phát bộ nhớ. Các dev của chúng ta nên tránh việc khởi tạo hay làm gì đó vô tội vạ trừ khi điều đó rất là cần thiết, bởi đơn giản, những điều đó có thể gây khó khăn cho chúng ta khi fix bug về sau và ảnh hưởng đến hiệu suất của ứng dụng.

// Playground

import UIKit
import Foundation

var str = "Hello, playground"
class InterViewTest {
    var name: String
    lazy var greeting: String = {
        [unowned self] in
        return "Hello (self.name) (self.appendedText)"
    }()
    
    lazy var appendedText = " and I am an iOS Developer"
    init(name: String) {
        self.name = name
    }
}

let testObj = InterViewTest(name: "ABBA")
testObj.greeting
testObj.appendedText

//

Trong Swift có một kỹ thuật gọi là lazy giúp cho bạn trì hoãn việc khởi tạo đối tượng cho đến khi nó thật sự được gọi đến. Nếu không được gọi đến, nó sẽ không bao giờ chạy. Chính vì lẽ đó, nó sẽ giúp bạn khá nhiều trong việc tiết kiệm thời gian xử lý trong app.

Tài liệu chính thức của Apple nói:

A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

Bạn cũng nên để ý rằng, luôn dùng từ khoá lazy cho các đối tượng là biến (với từ khoá var), bởi vì giá trị ban đầu của nó không được lấy ra cho đến khi việc khởi tạo giá trị hoàn tất. Các biến hằng số (constants) thì luôn luôn phải có giá trị trước khi việc khởi tạo hoàn tất nên thành ra không được khởi tạo với lazy.

Chúng ta cùng xem ví dụ dưới đây: Hãy tưởng tượng rằng bạn có 1 structInterviewCandidate. Nó có 1 biến optional bool để kiểm tra là thí sinh này (candidate) apply cho vị trí iOS hay Android. Biến string để mô tả hồ sơ cá nhân của thí sinh này được khai báo với từ khoá lazy. Nhìn vào dòng code dưới đây bạn sẽ hiểu, thí sinh apply ở vị trí iOS và biến giá trị lazy iOSResumeDescription sẽ được khởi tạo khi gọi print, còn biến androidResumeDescription sẽ bị nil.

import UIKit


struct InterviewCandidate {
    var isiOS:Bool?
    
    lazy var iOSResumeDescription: String = {
        return "I am an iOS developer"
    }()
    lazy var androidResumeDescription: String = {
        return "I am an android developer"
    }()
}

var person1 = InterviewCandidate()
person1.isiOS = true

if person1.isiOS! {
    print(person1.iOSResumeDescription)
} else {
    print(person1.androidResumeDescription)

}

 Đây là một ví dụ khá đơn giản. Nếu chúng ta có một class hoặc struct phức tạp chứa các biến được tính toán trả về kết quả từ hàm đệ quy và nếu chúng ta tạo 1000 đối tượng này, thì hiệu năng và bộ nhớ sẽ bị ảnh hưởng.

Lazy Stored Property vs Stored Property

Có một vài lợi ích mà 1 biến lazy nhỉnh hơn so với các biến thông thường:

1. Closure đi kèm với các biến lazy chỉ được thực thi khi bạn sử dụng biến ấy. Vì thế, nếu như vì vài lí do nào đó nó không được sử dụng thì bạn sẽ tránh được việc phân bổ và khởi tạo không cần thiết cho app.

2. Bạn có thể gán giá trị 1 biến đã được lưu trữ vào 1 biến lazy

3. Bạn có thể dùng self bên trong closure của biến lazy. Nếu như dùng self bên trong closure của lazy, nên để ý và khai báo thêm [unowned self] để tránh việc làm tăng strong reference count lên 1.

// playground code
import UIKit
import Foundation
class InterviewTest {
var name: String
lazy var greeting : String = { return “Hello (self.name)” }()
// No retain cycles here ..
init(name: String) {
self.name = name
 }
}
let testObj = InterviewTest(name:”abhi”)
testObj.greeting

Bạn có thể tham chiếu các biến bất kể bạn có sử dụng closure hay không:

lazy var iOSResumeDescription = “I am an iOS developer”

Lưu ý:

Các bạn phải nhớ rằng, mục đích chính của biến lazy là nó chỉ được compiler thực thi khi nó được gọi, sau đó thì giá trị sẽ được lưu lại. Vì thế, nếu bạn gọi iOSResumeDescription lần nữa, thì bạn vẫn sẽ thấy giá trị cũ trước đó.

Các quy tắc của Lazy:

  • Bạn không thể dùng lazy với let
  • Bạn chỉ có thể dùng lazy bên trong structclass
  • Biến lazy không được khởi tạo lúc ban đầu nên nó sẽ gây không an toàn cho thread
  • Bạn không thể dùng lazy chung với computed properties. Bởi vì các computed properties thường trả về giá trị mỗi lần chúng ta truy cập tới sau khi chúng thực thi các dòng code bên trong block.

Cảm ơn các bạn đã theo dõi bài viết này, các bạn có thể tìm hiểu nhiều hơn các bài viết về iOS tại blog của Techmaster, và các khóa học được xây dựng đáp ứng dựa trên xu thế công nghệ năm 2019

Techmaster có khóa học iOS Swift, React Native, Flutter ngắn hạn và dài hạn . 

Bài viết được tham khảo tại medium

0