11. Closure là kiểu tham chiếu:

Trong ví dụ ở trên, “incrementySeven” và “incrementByTen” là các hằng số những closure mà các hằng số này gọi đến vẫn có thể làm tăng biến “runningTotal” mà nó nắm bắt. Đó là bởi vì hàm và closure là các kiểu tham chiếu.

Bất kỳ khi nào bạn phân chia 1 hàm hoặc 1 closure là 1 hằng hoặc 1 biến, thực tế là bạn đã cài đặt hằng hoặc biến đó là 1 tham chiếu cho hàm hoặc closure. Lựa chọn trong ví dụ ở trên là ‘incrementByTen” gọi đến là 1 hằng số chứ không phải nội dung của chính closure đó

Điều này có nghĩa nếu bạn phân chia 1 closure cho 2 hằng số hoặc 2 biến khác nhau, cả 2 sẽ cùng gọi đến chung 1 closure

Ví dụ ở trên chỉ ra rằng việc gọi ‘alsoIncrementByTen” cũng giống như gọi “incrementByTen”. Bởi vì cả 2 đều gọi đến cùng 1 closure, cả 2 cùng tăng và cùng trả về cùng 1 “runningTotal”

12. Thoát khỏi closure:

1 closure được coi là thoát khỏi 1 hàm khi closure đó được truyền như 1 đối số của hàm, nhưng lại được gọi sau khi hàm trả về giá trị. Khi bạn khai báo 1 hàm với đối số là 1 closure, bạn có thể viết “@escaping” trước kiểu dữ liệu của biến đó để chỉ ra rằng closure đó được cho phép thoát.

1 cách để closure có thể thoát đó là nó được lưu trữ trong1 biến được định nghĩa bên ngoài hàm. Ví dụ như nhiều hàm bắt đầu bằng những thao tác bất dồng bộ như việc lấy đối số closure như 1 trình xử lý hoàn thành. Hàm sẽ trả về sau khi bắt đầu thao tác, nhưng closure sẽ không được gọi cho đến khi thao tác đó hoàn tất – closure cần phải được giải thoát để gọi lại về sau. Đây là ví dụ:

Hàm “someFunctionWithEscapingClosure( _ ; )” lấy closure là đối số và thêm nó vào 1 mảng được khai báo bên ngoài hàm. Nếu bạn không đánh dấu tham số của hàm với từ khóa “@escaping”, bạn sẽ gặp lỗi “compile-time”

Tạo ra 1 closure với từ khóa “@escaping” có nghĩa là bạn phải dứt khoát gọi đến “self” ở trong closure. Ví dụ trong đoạn code bên dưới, closure dược truyền vào hàm “someFunctionWithEscapingClosure( _ ; )” là 1 escaping closure, điều này có nghĩa cần phải gọi đến “self”. Ngược lại, closure được truyền vào “someFunctionWithNonescapingClosure(_:)”  là 1 nonescaping closure, điều này có nghĩa là không cần gọi đến “self”

13. Autoclosure:

Autoclosure là 1 closure được tự động tạo ra trong bao đóng của biểu thức mà biểu thức đó được truyền vào như 1 đối số của hàm. Nó không có bất kỳ đối số nào và khi nó được gọi đến, nó sẽ trả về giá trị của biểu thức mà đã bao bọc nó. Cú pháp thuận tiện này cho phép bạn bỏ qua các dấu ngoặc xung quanh các tham số của hàm được viết bởi biểu thức thông thường thay vì 1 closure đầy đủ.

Có cách rất phổ biến để gọi các hàm chứa autoclosure nhưng cách này không phù hợp để giải quyết các loại hàm khác. Ví dụ, hàm “assert(condition:message:file:line:) ” chứa 1 autoclosure cho các tham số “condition” và “message” của nó. Tham số “condition” chỉ được đánh giá trong khi debug và tham số “parameter” chỉ được đánh giá nếu “condition” là “false”

Autoclosure cho phép bạn trì hoãn việc đánh giá bởi vì đoạn code bên trong sẽ không chạy cho đến khi bạn gọi hàm closure. Việc trì hoãn đánh giá này rất hữu ích cho đoạn code có tác dụng phụ hoặc các đoạn code cần nhiều sự tính toán bởi vì nó cho phép bạn kiểm soát đến khi đoạn code được đánh giá. Đoạn code dưới đây mô ta việc trì hoãn đánh giá trong closure

Thậm chí khi phần tử đầu tiên của mảng “customersInLine” bị xóa đi bởi đoạn code trong hàm closure, phần tử này của mảng vẫn không bị xóa cho đến khi hàm closure thực sự được gọi đến. Nếu closure không bao giờ được gọi, biểu thức bên trong sẽ không bao giờ được thực thi, cũng có nghĩa là phần tử của mảng sẽ không bao giờ bị xóa. Chú ý rằng kiểu dữ liệu của “customerProvider” không phải là String mà là 1 hàm không có tham số nhưng trả về String “() -> String”.

Bạn có cách trình bầy khác về việc trì hoãn thực thi khi bạn truyền closure như 1 đối số của hàm

  • Chú ý: Việc lạm dụng quá nhiều autoclousre có thể làm code của bạn trở nên khó hiểu. Tên hàm và ngữ cảnh cần được làm rõ ràng để việc đánh giá đang được hoãn lại.

Hàm “serve(customer ; )” trong danh sách trên có 1 closure đầy đủ trả về tên của 1 khách hàng. Phiên bản bên dưới của hàm này thực hiện 1 thao tác tương tự nhưng thay vì lấy 1 hàm closure đầy đủ, nó lại lấy 1 autoclosure bằng cách đánh dấu kiểu dữ liệu của tham số với thuộc tính “@autoclosure”. Bây giờ bạn có thể gọi hàm này như thể nó là đối số dạng String thay vì là 1 closure. Đối số được từ động chuyển đổi thành 1 closure bởi vì kiểu tham số “customerProvider” được đánh dấu bởi thuộc tính “@autoclosure”.

Nếu bạn muốn autoclosure cho phép việc chạy thoát, hãy sử dụng cả 2 thuộc tính”@autoclosure” và “@escaping”. Thuộc tính “@escaping” đã được miêu tả trong phần Escaping Closures

Trong đoạn code trên, thay vì gọi đến hàm closre được truyền vào nó như đối số “customerProvide”, hàm “collectCustomerProvider( _ : )” đã thêm vào closure mảng “customerProvider”. Mảng này được khai báo bên ngoài phạm vi hàm, có nghĩa là các closure trong mảng có thể được thực thi sau khi hàm đã trả về. Kết quả là giá trị của đối số “customerProvider” phải được cho phép thoát khỏi phạm vi của hàm.

(Hết)

Link phần 1: https://techmaster.vn/posts/35246/tim-hieu-ve-closure-trong-swift-phan-1

Link phần 2: https://techmaster.vn/posts/35247/tim-hieu-ve-closure-trong-swift-phan-2

Link phần 3: https://techmaster.vn/posts/35248/tim-hieu-ve-closure-trong-swift-phan-3