Swift—Closure

by NhatHM
477 views

Khi mới làm quen với Swift, đôi khi ta gặp phải những đoạn code như dưới:

Client Side Swift — iOS
Server Side Swift — Vapor

Tuy nhiên ta không hiểu chúng là gì, và dùng như nào. Trong Swift, những đoạn code kiểu như trên được gọi là Closure, bài Note này sẽ đi sâu vào bới móc xem Closure là gì ;))

Trong Swift thì Closure là khái niệm khá quan trọng, ứng dụng nhiều cũng như là khó đọc cho người mới nếu chưa nắm được syntax của nó. Bài viết này sẽ nói về khái niệm Closure cũng như một vài ứng dụng của nó.

Closure là?

Closure là một block code, có thể tách ra để tái sử dụng. Hiểu đơn giản hơn thì Closure là function, nhưng khuyết danh. Ta có thể gán Closure vào biến và sử dụng như các kiểu value khác.

Closures có thể là 1 trong 3 loại sau:

  • Global functions: là closures có tên và không “capture” các giá trị.
  • Nested functions: là closures có tên và có thể “capture” các giá trị từ function chứa nó.
  • Closure expressions: là closures không có tên được viết dưới dạng giản lược syntax và có thể “capture” các giá trị từ các bối cảnh xung quanh.

Capturing value:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID103

2 loại đầu được gọi với cái tên khác là function. Và khi nhắc đến Closures, thường ta chỉ quan tâm đến trường hợp thứ 3, Closure expressions (từ giờ chỉ gọi tắt là Closure).

Closure Syntax

Ví dụ cụ thể về khai báo Closure:

import Foundation
// Declare a variable to hold a closure
var add: (Int, Int) -> Int
// Assign a closure to a variable
add = { (a: Int, b: Int) -> Int in
return a + b
}
// Or combine like this
var sub = { (a: Int, b: Int) -> Int in
return a – b
}
add(1, 2)
sub(1, 2)

Note: parameters trong Closure có thể là kiểu “in-out”, variadic, tuples, nhưng không thể có default value.

So với Function thì Closure đã được viết ra với mục đích ngắn gọn nhất có thể, và nó như sau:

// Declare a variable to hold a closure
var add: (Int, Int) -> Int
/** SHORTHAND SYNTAX **/
// Not need return keyword when only have single return statement
add = {(a: Int, b: Int) -> Int in
a + b
}
add(1, 1)
// Remove return type and parameters type
// Because we already declare: var add: (Int, Int) -> Int
add = {(a, b) in
a + b
}
add(9, 2)
// Remove parameters, Swift will refer parameters by number, start from 0:
add = {
$0 + $1
}
add(99, 1)

Ứng dụng của Closure:

Closure như là parameter cho function, Trailing closure syntax

Với function thì ta hoàn toàn có thể truyền vào cho function khác dưới dạng arguments, tuy nhiên trước khi truyền thì phải define function sẽ dùng làm argument:

Ví dụ dùng function làm parameter cho function

Đối với Closure thì đơn giản hơn, ta có thể define closure inline:

Ví dụ dùng closure làm parameter cho function
Note: {$0 + $1}: Swift cho phép refer đến mỗi parameter bằng format như bên, bắt đầu từ index 0.

Đối với những function có parameter cuối cùng là Closure, thì ta có thể viết lại function call dưới dạng Trailing Closure như sau:

Trailing Closure example

Cách làm này phù hợp với những Closure dài, dùng như complete block/callback…Ví dụ về cách sử dụng callback/block khi request data từ server, ví dụ:

Objective C:

Khai báo method:

Gọi hàm:

Swift Closure:

Khai báo func:

Gọi hàm:

Các ứng dụng của Closure nên biết:

sorted(): dùng để thay đổi điều kiện sort cho array/collection…

filter(): dùng dể lọc các phần tử của collection/array với điều kiện nhất định, ví dụ như lọc tuổi người dùng để kiểm tra nhưng ai đủ tuổi xem 18+ chẳng hạn

map(): dùng để áp dụng điều kiện nào đó cho tất cả các item trong array/collection, ví dụ tính tiền của sản phẩm sau khi áp dụng thuế tiêu thụ chẳng hạn

reduce(): dùng để tính tổng của array…Xem ví dụ bên dưới, bài toán là cần tính tổng tất cả các sách có trong kho, mỗi record sách được lưu dưới dạng: tên sách, số lượng, và giá.

reduce() có 2 parameters là giá trị ban đầu, tức là giá trị kết quả giả định ban đầu, ở ví dụ này được set là 0, và 1 closure tính toán trả về giá trị cần tính, giá trị này tiếp theo sẽ được truyền vào closure dưới dạng first parameter ($0), và lặp lại cho đến hết array.

Với ví dụ ở trên thì reduce() sẽ xử lý như sau

Step1. $0 sẽ có giá trị là 0 (chính là giá trị của init result). $1 là object đầu tiên của array books. Closure này sẽ return kết quả là $0 (=0)+ (12 * 39.000) = 468.000. Kết quả này sẽ được gán cho $0 của step tiếp theo. Nếu truyền vào giá trị ban đầu là số khác, thì $0 cũng tương đương với giá trị của số đó. Ví dụ ở trên, nếu truyền vào là books.reduce(5)… thì closure sẽ return kết quả là $0 (=5)+ (12 * 39.000)

Step2. $0 sẽ có giá trị là kết quả của step 1, và $1 là object ở index = 1 của array books. Kết quả return của Closure là $0 (=468.000) + (9 * 22.000). Step này sẽ được lặp lại cho đến hết array books. Về cơ bản, step 1 và 2 chỉ khác nhau ở chỗ, ở step 1, $0 chưa có giá trị, nên nó sẽ được chỉ định là giá trị của parameter thứ nhất của Closure reduce(), a hi hi hi.


Closure được dùng rất nhiều trong source code Swift, nếu biết syntax thì sẽ dễ hơn trong việc đọc source code, nắm được các ưu điểm và ứng dụng đúng lúc sẽ đem lại source code ngắn gọn và hiệu quả hơn…

Note: Một vài gist về function và closure.

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.