Điều gì có nghĩa là đóng cửa @escaping và @nonescaping trong Swift?

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

Đóng cửa là gì?

Đóng là các khối chức năng khép kín có thể được truyền qua và sử dụng trong mã của bạn.

Trong Swift 1.x và Swift 2.x, tham số đóng là @escaping theo mặc định, có nghĩa là đóng có thể thoát trong quá trình thực thi thân hàm. nếu don khác muốn thoát các tham số đóng, đánh dấu nó là @nonescaping.

Trong Swift 3.x, Apple đã thực hiện một thay đổi: các tham số đóng trở thành @nonescaping theo mặc định, việc đóng cũng sẽ được thực thi với thân hàm, nếu muốn thoát lệnh thực thi đóng, hãy đánh dấu là @escaping. Tại sao Apple thực hiện thay đổi này? Hãy cùng thảo luận về vấn đề này khi kết thúc cuộc thảo luận.

1. Đóng cửa @nonescaping:

Khi truyền một bao đóng làm đối số hàm, bao đóng sẽ được thực thi với thân hàm hàm và trả về trình biên dịch trở lại. Khi việc thực thi kết thúc, việc đóng thông qua vượt ra khỏi phạm vi và không còn tồn tại trong bộ nhớ.

Vòng đời của việc đóng @nonescaping:
 1. Vượt qua bao đóng dưới dạng đối số hàm, trong khi gọi hàm.
 2. Làm một số công việc bổ sung với chức năng.
 3. Chức năng chạy đóng cửa.
 4. Hàm trả về trình biên dịch trở lại.

Thí dụ:

func getSumOf (mảng: [Int], handler: ((Int) -> Void)) {
        //bước 2
        tổng var: Int = 0
        cho giá trị trong mảng {
            tổng + = giá trị
        }
        
        //bước 3
        xử lý (tổng hợp)
    }
    
    func doS Something () {
        // đặt 1
        self.getSumOf (mảng: [16,756,442,6,23]) {[tự yếu] (tổng hợp) trong
            in (tổng hợp)
            // bước 4, kết thúc thực hiện
        }
    }
// Nó sẽ in sumof tất cả các số đã cho.

Ở đây, chúng ta chỉ gọi hàm với một bao đóng, được thực thi ở phần cuối của thân hàm Hàm.
Vì vậy, chúng tôi không thoát khỏi việc thực hiện đóng cửa. Vì bước 4 được thực hiện đóng sẽ không có sự tồn tại trong bộ nhớ.

2. Đóng cửa @escaping:

Khi chuyển một bao đóng làm đối số hàm, bao đóng đang được bảo toàn để thực thi sau và thân hàm Hàm được thực thi, trả về trình biên dịch trở lại. Khi việc thực thi kết thúc, phạm vi của bao đóng đã qua tồn tại và tồn tại trong bộ nhớ, cho đến khi đóng được thực thi.
 Có một số cách để thoát khỏi việc đóng cửa:

  • Lưu trữ: Khi bạn cần duy trì việc đóng trong bộ lưu trữ tồn tại trong bộ nhớ, quá khứ của chức năng gọi sẽ được thực thi và trả lại trình biên dịch. (Giống như chờ phản hồi API)
  • Thực thi không đồng bộ: Khi bạn đang thực hiện việc đóng không đồng bộ trên hàng đợi gửi hàng, hàng đợi sẽ giữ việc đóng trong bộ nhớ cho bạn, sẽ được sử dụng trong tương lai. Trong trường hợp này, bạn không biết khi nào việc đóng sẽ được thực thi.

Khi bạn sẽ cố gắng sử dụng bao đóng với các tùy chọn này, trình biên dịch swift sẽ hiển thị lỗi.

Vòng đời của việc đóng @escaping:
1. Vượt qua bao đóng dưới dạng đối số hàm, trong khi gọi hàm.
2. Làm một số công việc bổ sung trong chức năng.
3. Chức năng thực hiện đóng không đồng bộ hoặc được lưu trữ.
4. Hàm trả về trình biên dịch trở lại.

Ví dụ 1 (Lưu trữ):

var khenitionHandler: ((Int) -> Void)?
    func getSumOf (mảng: [Int], handler: @escaping ((Int) -> Void)) {
        //bước 2
       // ở đây tôi đang lấy vòng lặp chẳng hạn, trong trường hợp thực tế, nó sẽ là một thứ khác như lệnh gọi API
       tổng var: Int = 0
        cho giá trị trong mảng {
            tổng + = giá trị
        }
//bước 3
        self.complitionHandler = xử lý
    }
    
    func doS Something () {
        // đặt 1
        self.getSumOf (mảng: [16,756,442,6,23]) {[tự yếu] (tổng hợp) trong
            in (tổng hợp)
            // bước 4, kết thúc thực hiện
        }
    }
// Ở đây chúng tôi đang lưu trữ việc đóng cửa để sử dụng trong tương lai.
// Nó sẽ in sumof tất cả các số đã qua.

Ví dụ 2 (Thực thi không đồng bộ):

func getSumOf (mảng: [Int], handler: @escaping ((Int) -> Void)) {
        //bước 2
        tổng var: Int = 0
        cho giá trị trong mảng {
            tổng + = giá trị
        }
        //bước 3
        Globals.delay (0,3, đóng cửa: {
            xử lý (tổng hợp)
        })
    }
    
    func doS Something () {
        // đặt 1
        self.getSumOf (mảng: [16,756,442,6,23]) {[tự yếu] (tổng hợp) trong
            in (tổng hợp)
            // bước 4, kết thúc thực hiện
        }
    }
// Ở đây chúng tôi đang gọi đóng cửa với độ trễ 0,3 giây
// Nó sẽ in sumof tất cả các số đã qua.

Ở đây chúng tôi có ý nghĩa của thuộc tính @escaping. Vì vậy, khi bạn cần thoát khỏi việc thực thi bao đóng, hãy sử dụng thuộc tính @escaping trong Swift 3. Lỗi hiển thị ở trên của trình biên dịch sẽ biến mất sau khi thực hiện đóng như thoát bằng cách sử dụng thuộc tính @escaping.

Tại sao họ làm @nonescaping theo mặc định?

Có nhiều lợi ích khác nhau của việc không thoát như mặc định. Các lợi ích quan trọng nhất là hiệu suất và tối ưu hóa mã bởi trình biên dịch, bởi vì nếu trình biên dịch biết rằng bao đóng không thoát, sẽ quan tâm đến việc cấp phát bộ nhớ cho bao đóng.

Và một điều nữa là, chúng ta có thể sử dụng bản thân mà không gặp vấn đề gì trong các lần đóng không thoát vì việc đóng thực thi trước khi hàm trả về nên chắc chắn bản thân sẽ ở đó. Chúng tôi không cần phải sử dụng bản thân yếu, đây là tính năng bổ sung của nó.

Trong Swift 3, các bao đóng không thoát theo mặc định, có thể được sử dụng @escaping nếu không phải là những gì chúng ta muốn. Việc đóng cửa không thoát được thách thức sẽ thực thi trước khi hàm trả về.
Luôn nhớ sử dụng bản thân yếu trong khi sử dụng đóng cửa.

Cảm ơn bạn đã đọc, vui lòng nhấn biểu tượng giới thiệu nếu thích bộ sưu tập này. Câu hỏi? Để lại trong bình luận.