07/09/2018, 17:11

Swift - Closure: Bài 1: Closure cơ bản

Swift - Closure: Bài 1: Closure cơ bản Hôm nay, mình xin đổi gió một chút, để nhân tiện ôn lại phần closure. Closure cơ bản 1. Đặt vấn đề Closure là gì? Định nghĩa các kiểu thì mình không đề cập nhiều nữa, trên mạng có rồi. Mình sẽ dựa vào những ví dụ thực tế để giải thích ý nghĩa. Tất ...

Swift - Closure: Bài 1: Closure cơ bản

Hôm nay, mình xin đổi gió một chút, để nhân tiện ôn lại phần closure.

Closure cơ bản
1. Đặt vấn đề
Closure là gì? Định nghĩa các kiểu thì mình không đề cập nhiều nữa, trên mạng có rồi. Mình sẽ dựa vào những ví dụ thực tế để giải thích ý nghĩa. Tất nhiên mọi thứ mình trình bày là dưới góc nhìn một newbie.

Các bạn xem ví dụ sau:

var listOfPlayers: [String] = ["Ozil","Santi","Wilshere","Ramsey","Welbeck"]
func takePenalty(_ player: String) {
    var isReady = false
    isReady = arc4random() % 2 == 0 ? true:false
    if isReady { print("Goal of: (player)") }
    else { print("Nooo") }
}
while(listOfPlayers.count>0) {
    takePenalty(listOfPlayers.removeFirst())
}

Mình làm một ví dụ sau, giả sử trọng tài có 1 phút chuẩn bị (kiểu như kiểm tra lưới, bóng v..v). Nếu trọng tài chuẩn bị xong, mới cho cầu thủ lên thực hiện sút. Như vậy nhìn vào dòng code while(), mình thấy nếu như chuẩn bị xong rồi cầu thủ lên sút, sút xong đi chỗ khác để người tiếp theo lên (removeFirst()). Khá hợp lí, xem kết quả thử:

Goal of: Ozil
Goal of: Santi
Goal of: Wilshere
Nooo
Nooo

Như vậy sai rồi, các bạn thấy chỗ sai chưa? Mình thấy goài, do listOfPlayers nó cắt phần tử ra, sau đó mới thấy thực hiện phần tử đó trong hàm takePenalty. Nên dù true hay false nó vẫn sẽ cho qua, rồi lấy tiếp phần tử tiếp theo.

2. Fix
Bây giờ, các bạn thử đoạn code sau:

var listOfPlayers: [String] = ["Ozil","Santi","Wilshere","Ramsey","Welbeck"]
func takePenalty(_ player: () -> String) {
    var isReady = false
    isReady = arc4random() % 2 == 0 ? true:false
    if isReady { print("Goal of: (player())") }
    else { print("Nooo") }
}
while(listOfPlayers.count>0) {
    takePenalty({()->String in
        listOfPlayers.removeFirst()}
     )
}

Các bước như sau:
1.Nếu listOfPlayer lớn hơn không thì thực hiện hàm takePenalty

2.Trong hàm takePenalty, ta đợi trọng tài chuẩn bị. Đến đây có hai trường hợp:

+Nếu chuẩn bị xong (true), nó thì hiện print -> trong print có cái closure -> nó thực hiện cái closure này -> xuống closure ta thấy nó cắt giá trị đầu tiền ra để trả về (như vậy ta có cái tên đầu tiên) -> xong ghép vào "Goal of Ozil" chẳng hạn.

+Nếu chuẩn bị chưa xong (false), nó sẽ chuyển qua else, mà như các bạn thấy, trong phần else ko có closure -> không cắt phần tử đầu tiên ra mà in thẳng chữ Noo. Sau khi xong phần Noo này, nó xong nhiệm vụ mà phần tử đầu tiên vẫn được giữ lại.

3.Quay lại vòng lặp

Kết quả sẽ là:

Nooo
Goal of: Ozil
Nooo
Nooo
Goal of: Santi
Goal of: Wilshere
Nooo
Nooo
Goal of: Ramsey
Goal of: Welbeck

Sự khác biệt là gì? Ở phần đầu tiên, ta thấy ta gọi cầu thủ lên trước khi trọng tài kiểm tra. Kiểm tra xong có true/ false gì cũng bảo cầu thủ đó đi chỗ khác. Phần sau, vẫn vòng lặp đó nhưng trọng tài muốn kiểm tra xong, nếu đúng mới gọi cầu thủ kia lên, không thì thôi.

Tóm lại, closure mình hiểu nó kiểu như function, như ví dụ trên, mình truyền vào nó 1 function lấy tên người đầu tiên, và mình muốn khi nào true mới gọi function thực hiện việc đó.

3. Autoclosure
Nhân tiện mình nói thêm 1 chút về Autoclosure, refactor đoạn code như sau:

var listOfPlayers: [String] = ["Ozil","Santi","Wilshere","Ramsey","Welbeck"]
func takePenalty(_ player: @autoclosure () -> String) {
    var isReady = false
    isReady = arc4random() % 2 == 0 ? true:false
    if isReady { print("Goal of: (player())") }
    else { print("Nooo") }
}
while(listOfPlayers.count>0) {
    takePenalty(listOfPlayers.removeFirst()})
}

Nhìn đẹp hơn đúng không mọi người, function work như bình thường. Các bạn để ý closure của mình là một dạng () -> String, hay tổng quát hơn là () -> Any. Không có argument trong function và trả về 1 kiểu bất kì. Thì khi đó takePenalty() sẽ hiểu là cái này sẽ thực hiện cái gì đó và chắc chắn là trả về kiểu String cho mình.

Nên mình sử dụng autoclosure để khỏi cần gõ () -> String in v..v. Qua phần tiếp mình sẽ ứng dụng closure vào 1 phần rất hay đó là callback/ completion.

0