Swift - Closure: Bài 1: Closure cơ bản
swift
68
Closure
6
ios
54
Male avatar

Bùi Khánh Duy viết ngày 30/06/2018

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

=====
Updated ngày 30/06

Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalog, mình mới dọn về nhà mới ở đây, thoải mái, đẹp đẽ rõ ràng hơn kipalog. Mong các bạn có thể theo dõi tại trang web của mình để cập nhật thêm nha

Mình mới viết một loạt bài về chủ đề Clean Architecture và Service Locator. Các bạn có thể theo dõi.
Page trên facebook ở đây: ở đây

=====
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.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

Bùi Khánh Duy

29 bài viết.
10 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
3 3
RxSwift: Bài 1 Observable và Just, Of, From ===== Updated ngày 30/06 Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalo...
Bùi Khánh Duy viết 5 tháng trước
3 3
Male avatar
3 0
Autolayout và lifecycle trong IOS ===== Updated ngày 30/06 Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalog, mình mớ...
Bùi Khánh Duy viết 3 tháng trước
3 0
Male avatar
2 3
RxSwift: Bài 2 Subscribing to observables ===== Updated ngày 30/06 Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalog,...
Bùi Khánh Duy viết 5 tháng trước
2 3
Bài viết liên quan
White
2 3
Xin chào mọi người. Mình xin chia sẽ một UILabel Helper nhỏ dùng trong truờng hợp cần tính chiều cao của UILabel để xác định "Show More" button có ...
DonDinh viết 3 năm trước
2 3
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
29 bài viết.
10 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!