+3

Cách sử dụng @escaping và @noescape trong closures

Trong quá trình code, khi bạn đang làm việc với các function, có thể đã chạy với thuộc tính @escaping hoặc @noescape. Bạn đã bao giờ dành thời gian để suy nghĩ 🤔 về ý nghĩa của nó? Vì vậy, ở đây chúng ta sẽ thảo luận về các điều này.

1. @noescape closures

@noescape mặc định khi tạo closure. Khi bạn gọi 1 function, việc xử lý được thực thi trong function và trả về kết quả. Khi việc thực thi kết thúc, closure sẽ được remove khỏi memory. Được dùng khi bạn muốn gọi function trả về kết quả ngay lập tức sau khi gọi và không quan tâm tới việc quản lý memory.

Lifecycle:

  1. Gọi function có closure.
  2. Xử lý trong function.
  3. Chạy closure.
  4. Return về trình biên dịch.
func getSumOf(array:[Int], handler: ((Int)->Void)) {
        //step 2
        var sum = 0
        for value in array {
            sum += value
        }
        
        //step 3
        handler(sum)
  }
    
   override func viewDidLoad() {
        super.viewDidLoad()
        //setp 1
         self.getSumOf(array: [1,2,3,4,5]) { (sum) in
            print(sum)
            //step 4
        }
    }

Tại bước thứ 4 closure sẽ không còn tồn tại trong memory.

2. @escaping closures:

Khi bạn gọi 1 function, closure được giữ để được thực thi sau và body của function được thực thi, trả về trình biên dịch. Khi function thực thi kết thúc, closure tồn tại trong bộ nhớ, cho đến khi closure được thực thi.

  • Storage: Khi bạn cần phải giữ closure lưu trữ tồn tại trong bộ nhớ, đợi hoàn thành việc nào đó mới thực thi closure và trả về kết quả. (Giống như chờ phản hồi API)
  • Asynchronous: Khi bạn đang thực hiện closure không đồng bộ trên hàng đợi, hàng đợi sẽ giữ closure trong memory cho bạn, để được sử dụng trong tương lai.

Lifecycle:

  1. Gọi function có closure.
  2. Xử lý trong function.
  3. Function thực thi closure asynchronous hoặc storage.
  4. Return về trình biên dịch.

Storage

var complitionHandler: ((Int)->Void)?
    func getSumOf(array:[Int], handler: @escaping ((Int)->Void)) {
        //step 2
       //here I'm taking for loop just for example, in real case it'll be something else like API call
       var sum: Int = 0
        for value in array {
            sum += value
        }
        //step 3
        self.complitionHandler = handler
    }
    
    func doSomething() {
        //setp 1
        self.getSumOf(array: [16,756,442,6,23]) { (sum) in
            print(sum)
            //step 4, finishing the execution
        }
    }
//Here we are storing the closure for future use.
//It will print the sumof all the passed numbers.

Asynchronous

func getSumOf(array:[Int], handler: @escaping ((Int)->Void)) {
        //step 2
        var sum: Int = 0
        for value in array {
            sum += value
        }
        //step 3
        Globals.delay(0.3, closure: {
            handler(sum)
        })
    }
    
    func doSomething() {
        //setp 1
        self.getSumOf(array: [16,756,442,6,23]) { (sum) in
            print(sum)
            //step 4, finishing the execution
        }
    }
//Here we are calling the closure with the delay of 0.3 seconds
//It will print the sumof all the passed numbers.

3. Why Is This Important?

Có một số lợi ích để tạo @noescape closure theo mặc định. Những lợi ích rõ ràng nhất là hiệu năng và khả năng cho trình biên dịch tối ưu code của bạn. Nếu trình biên dịch biết rằng @noescape closure, nó có thể xử lý rất nhiều chi tiết về quản lý memory.

Điều này cũng có nghĩa là bạn có thể sử dụng từ khóa self mà không có vấn đề trong @noescape vì closure được gọi trước khi hàm trả về. Không cần phải sử dụng weak reference self trong closure.

Nguồn:


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí