Từ khóa bảo vệ của Swift


197

Swift 2 đã giới thiệu guardtừ khóa, có thể được sử dụng để đảm bảo rằng các dữ liệu khác nhau được cấu hình sẵn sàng hoạt động. Một ví dụ tôi thấy trên trang web này thể hiện chức năng submitTapped:

func submitTapped() {
    guard username.text.characters.count > 0 else {
        return
    }

    print("All good")
}

Tôi tự hỏi nếu sử dụng guardcó gì khác hơn là làm theo cách cũ, sử dụng một ifđiều kiện. Liệu nó mang lại lợi ích, mà bạn không thể có được bằng cách sử dụng một kiểm tra đơn giản?


Đồng thời xem câu hỏi bảo vệ vs if-let
Honey

Vui lòng tham khảo liên kết sau Medium.com/@pvpriya7/swift-guard-18e59c50c624
Priyanka V

Câu trả lời:


368

Đọc bài viết này tôi nhận thấy những lợi ích tuyệt vời khi sử dụng Guard

Ở đây bạn có thể so sánh việc sử dụng bảo vệ với một ví dụ:

Đây là phần không có người bảo vệ:

func fooBinding(x: Int?) {
    if let x = x where x > 0 {
        // Do stuff with x
        x.description
    }

    // Value requirements not met, do something
}
  1. Ở đây bạn đang đặt mã mong muốn của mình trong tất cả các điều kiện

    Bạn có thể không thấy ngay vấn đề này, nhưng bạn có thể tưởng tượng nó sẽ trở nên khó hiểu như thế nào nếu nó được lồng với nhiều điều kiện cần phải đáp ứng trước khi chạy các câu lệnh của bạn

Cách để làm sạch điều này là thực hiện từng kiểm tra trước và thoát nếu có bất kỳ kiểm tra nào. Điều này cho phép dễ dàng hiểu những điều kiện sẽ làm cho chức năng này thoát ra.

Nhưng bây giờ chúng ta có thể sử dụng bảo vệ và chúng ta có thể thấy rằng có thể giải quyết một số vấn đề:

func fooGuard(x: Int?) {
    guard let x = x where x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
    x.description
}
  1. Kiểm tra điều kiện bạn muốn, không phải điều kiện bạn không muốn. Điều này một lần nữa tương tự như một khẳng định. Nếu điều kiện không được đáp ứng, câu lệnh khác của người bảo vệ được chạy, nó sẽ thoát ra khỏi chức năng.
  2. Nếu điều kiện vượt qua, biến tùy chọn ở đây sẽ tự động được mở ra cho bạn trong phạm vi mà câu lệnh bảo vệ được gọi - trong trường hợp này là hàm fooGuard (_ :).
  3. Bạn đang sớm kiểm tra các trường hợp xấu, giúp chức năng của bạn dễ đọc hơn và dễ bảo trì hơn

Mẫu tương tự này cũng đúng với các giá trị không tùy chọn:

func fooNonOptionalGood(x: Int) {
    guard x > 0 else {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

func fooNonOptionalBad(x: Int) {
    if x <= 0 {
        // Value requirements not met, do something
        return
    }

    // Do stuff with x
}

Nếu bạn vẫn có bất kỳ câu hỏi nào, bạn có thể đọc toàn bộ bài viết: Tuyên bố bảo vệ Swift.

Kết thúc

Và cuối cùng, đọc và kiểm tra tôi thấy rằng nếu bạn sử dụng bảo vệ để mở khóa bất kỳ tùy chọn nào,

những giá trị chưa được bao bọc đó sẽ giúp bạn sử dụng trong phần còn lại của khối mã

.

guard let unwrappedName = userName else {
    return
}

print("Your username is \(unwrappedName)")

Ở đây, giá trị chưa được mở sẽ chỉ khả dụng trong khối if

if let unwrappedName = userName {
    print("Your username is \(unwrappedName)")
} else {
    return
}

// this won't work – unwrappedName doesn't exist here!
print("Your username is \(unwrappedName)")

3
Hey @Eric bạn đã làm một bài viết tuyệt vời! Cảm ơn bạn đã làm cho nó dễ hiểu!
Jorge Casariego

1
Tôi đang sử dụng bảo vệ để mở khóa NSError. Nhưng khi tôi cố gắng sử dụng nó trong phạm vi bảo vệ (ví dụ để chuyển một lỗi vào một số cuộc gọi lại) thì nó báo "Biến được khai báo trong điều kiện bảo vệ không thể sử dụng được trong cơ thể của nó". Nó có ý nghĩa gì không? Cảm ơn
GeRyCh

6
@GeRyCh hủy ghép nối trong câu lệnh bảo vệ làm cho biến đó có sẵn sau câu lệnh bảo vệ, không nằm trong nó. Mất một thời gian để tôi quen với điều này.
DonnaLea

2
Đây là một bài viết tuyệt vời khác về việc sử dụng bảo vệ để làm sạch các tùy chọn unrap. Tổng hợp nó lên độc đáo.
Doches

let x = x where x > 0có nghĩa là bạn đã kết hợp một điều kiện khác vào ràng buộc tùy chọn của bạn? Ý tôi là nó hơi khác so vớiif let constantName = someOptional { statements }
Honey

36

Không giống như if, guardtạo biến có thể được truy cập từ bên ngoài khối của nó. Nó rất hữu ích để mở ra rất nhiều Optionals.


24

Thực sự có hai lợi ích lớn guard. Một là tránh kim tự tháp diệt vong, như những người khác đã đề cập - rất nhiều if letcâu nói khó chịu được lồng vào nhau di chuyển ngày càng xa về phía bên phải.

Lợi ích khác thường là logic bạn muốn triển khai là if not let"nhiều hơn" if let { } else".

Đây là một ví dụ: giả sử bạn muốn thực hiện accumulate- giao thoa giữa mapreducenơi nó cung cấp cho bạn một mảng giảm chạy . Đây là với guard:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {
        // if there are no elements, I just want to bail out and
        // return an empty array
        guard var running = self.first else { return [] }

        // running will now be an unwrapped non-optional
        var result = [running]

        // dropFirst is safe because the collection
        // must have at least one element at this point
        for x in dropFirst(self) {
            running = combine(running, x)
            result.append(running)
        }
        return result
    }

}


let a = [1,2,3].accumulate(+)  // [1,3,6]
let b = [Int]().accumulate(+)  // []

Làm thế nào bạn sẽ viết nó mà không có bảo vệ, nhưng vẫn sử dụng firstmà trả về một tùy chọn? Một cái gì đó như thế này:

extension Sliceable where SubSlice.Generator.Element == Generator.Element {

    func accumulate(combine: (Generator.Element,Generator.Element)->Generator.Element) -> [Generator.Element] {

        if var running = self.first  {
            var result = [running]

            for x in dropFirst(self) {
                running = combine(running, x)
                result.append(running)
            }
            return result
        }
        else {
            return []
        }
    }

}

Việc lồng thêm rất khó chịu, nhưng cũng không hợp lý khi có sự khác biệt ifelsecách xa nhau. Sẽ dễ đọc hơn nhiều khi có lối ra sớm cho trường hợp trống, và sau đó tiếp tục với phần còn lại của hàm như thể đó không phải là một khả năng.


19

Khi một điều kiện được đáp ứng bằng cách sử dụng guardnó sẽ hiển thị các biến được khai báo trong guardkhối cho phần còn lại của khối mã, đưa chúng vào phạm vi của nó. Mà, như đã nêu trước đây, chắc chắn sẽ có ích với các if lettuyên bố lồng nhau .

Lưu ý rằng bảo vệ yêu cầu trả lại hoặc ném vào tuyên bố khác của nó.

Phân tích cú pháp JSON với Guard

Dưới đây là một ví dụ về cách người ta có thể phân tích cú pháp một đối tượng JSON bằng cách sử dụng bảo vệ thay vì if-let. Đây là một đoạn trích từ một mục blog bao gồm một tệp sân chơi mà bạn có thể tìm thấy ở đây:

Cách sử dụng Guard trong Swift 2 để phân tích JSON

func parseJSONWithGuard(data : [String : AnyObject]) throws -> Developer {

    guard let firstname = data["First"] as? String  else {
        return Developer() // we could return a nil Developer()
    }

    guard let lastname = data["Last"] as? String else {
        throw ParseError.BadName // or we could throw a custom exception and handle the error
    }

    guard let website = data["WebSite"] as? String else {
        throw ParseError.BadName
    }

    guard let iosDev = data["iosDeveloper"] as? Bool else {
        throw ParseError.BadName
    }



    return Developer(first: firstname, last: lastname, site: website, ios: iosDev)

}

tải sân chơi: sân chơi bảo vệ

Thêm thông tin:

Đây là một đoạn trích từ Hướng dẫn ngôn ngữ lập trình Swift:

Nếu điều kiện của câu lệnh bảo vệ được đáp ứng, việc thực thi mã sẽ tiếp tục sau dấu ngoặc đóng của câu lệnh bảo vệ. Bất kỳ biến hoặc hằng nào được gán giá trị bằng cách sử dụng một ràng buộc tùy chọn như một phần của điều kiện đều có sẵn cho phần còn lại của khối mã mà câu lệnh bảo vệ xuất hiện.

Nếu điều kiện đó không được đáp ứng, mã bên trong nhánh khác được thực thi. Chi nhánh đó phải chuyển điều khiển để thoát khỏi khối mã mà câu lệnh bảo vệ đó xuất hiện. Nó có thể thực hiện điều này bằng câu lệnh truyền điều khiển như trả về, ngắt hoặc tiếp tục hoặc có thể gọi một hàm hoặc phương thức không trả về như fatalError ().


7

Một lợi ích là loại bỏ rất nhiều if letbáo cáo lồng nhau . Xem video "Có gì mới trong Swift" của WWDC vào khoảng 15:30, phần có tiêu đề "Kim tự tháp của Doom".


6

Khi nào nên dùng vệ sĩ

Nếu bạn đã có bộ điều khiển chế độ xem với một vài thành phần UITextField hoặc một số loại đầu vào người dùng khác, bạn sẽ nhận thấy ngay rằng bạn phải mở khóa tùy chọn textField.text để đến văn bản bên trong (nếu có!). isEmpty sẽ không làm bạn tốt ở đây, nếu không có bất kỳ đầu vào nào, trường văn bản sẽ chỉ trả về nil.

Vì vậy, bạn có một vài trong số này mà bạn mở khóa và cuối cùng chuyển đến một chức năng đưa chúng đến điểm cuối của máy chủ. Chúng tôi không muốn mã máy chủ phải xử lý các giá trị không hoặc gửi nhầm các giá trị không hợp lệ đến máy chủ để chúng tôi sẽ hủy các giá trị đầu vào đó trước.

func submit() {
    guard let name = nameField.text else {
        show("No name to submit")
        return
    }

    guard let address = addressField.text else {
        show("No address to submit")
        return
    }

    guard let phone = phoneField.text else {
        show("No phone to submit")
        return
    }

    sendToServer(name, address: address, phone: phone)
}

func sendToServer(name: String, address: String, phone: String) {
  ...
}

Bạn sẽ nhận thấy rằng chức năng giao tiếp máy chủ của chúng tôi lấy các giá trị Chuỗi không tùy chọn làm tham số, do đó bảo vệ sẽ hủy kết nối trước. Việc hủy kết nối là một chút không trực quan bởi vì chúng ta đã quen với việc hủy kết nối nếu để cho phép hủy bỏ các giá trị để sử dụng trong một khối. Ở đây, câu lệnh bảo vệ có một khối liên kết nhưng thực ra nó là một khối khác - tức là việc bạn làm nếu hủy kết nối thất bại - các giá trị được mở thẳng vào cùng bối cảnh như chính câu lệnh.

// tách mối quan tâm

Không có người bảo vệ

Không sử dụng bảo vệ, chúng tôi sẽ kết thúc với một đống mã lớn giống như một kim tự tháp diệt vong . Điều này không có quy mô tốt để thêm các trường mới vào biểu mẫu của chúng tôi hoặc tạo mã rất dễ đọc. Việc thụt lề có thể khó theo dõi, đặc biệt với rất nhiều câu lệnh khác ở mỗi ngã ba.

func nonguardSubmit() {
    if let name = nameField.text {
        if let address = addressField.text {
            if let phone = phoneField.text {
                sendToServer(name, address: address, phone: phone)
            } else {
                show("no phone to submit")
            }
        } else {
            show("no address to submit")
        }
    } else {
        show("no name to submit")
    }
}

Có, chúng tôi thậm chí có thể kết hợp tất cả những điều này nếu để các câu lệnh thành một câu lệnh được phân tách bằng dấu phẩy nhưng chúng tôi sẽ mất khả năng tìm ra câu lệnh nào thất bại và đưa ra thông báo cho người dùng.

https://thatthinginswift.com/guard-statement-swift/


5

Với việc sử dụng bảo vệ cường độ của chúng tôi là rõ ràng. chúng tôi không muốn thực thi phần còn lại của mã nếu điều kiện cụ thể đó không được thỏa mãn. ở đây chúng tôi cũng có thể mở rộng chuỗi, vui lòng xem mã bên dưới:

guard let value1 = number1, let value2 = number2 else { return }
 // do stuff here

5

Tuyên bố bảo vệ sẽ làm. nó là một vài khác nhau

1) nó cho phép tôi giảm lồng nhau nếu câu
2) nó tăng phạm vi mà biến của tôi có thể truy cập được

nếu tuyên bố

func doTatal(num1 : Int?, num2: Int?) {
  // nested if statement
    if let fistNum = num1 where num1 > 0 {
        if let lastNum = num2 where num2 < 50 {

          let total = fistNum + lastNum
        }
    }
 // don't allow me to access out of the scope 
 //total = fistNum + lastNum 
}

Tuyên bố bảo vệ

func doTatal(num1 : Int?, num2: Int?) {
   //reduce  nested if statement and check positive way not negative way 
    guard let fistNum = num1 where num1 > 0 else{
      return
    }
    guard  let lastNum = num2 where num2 < 50 else {
     return
    }
    // increase my scope which my variable accessible
    let total = fistNum + lastNum

}

3

Từ tài liệu của Apple:

Tuyên bố bảo vệ

Một tuyên bố bảo vệ được sử dụng để chuyển điều khiển chương trình ra khỏi phạm vi nếu một hoặc nhiều điều kiện không được đáp ứng.

Synatx:

guard condition else {
    statements
}

Lợi thế:

1. Bằng cách sử dụng guardcâu lệnh, chúng ta có thể thoát khỏi các điều kiện lồng nhau sâu sắc với mục đích duy nhất là xác nhận một tập các yêu cầu.

2. Nó được thiết kế đặc biệt để sớm thoát khỏi một phương thức hoặc chức năng.

nếu bạn sử dụng nếu để bên dưới là mã trông như thế nào.

  let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

        if error == nil {
            if let  statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 {
                if let data = data {

                    //Process Data Here.
                    print("Data: \(data)")

                } else {
                    print("No data was returned by the request!")
                }
            } else {
                print("Your request returned a status code other than 2XX!")
            }
        } else {
            print("Error Info: \(error.debugDescription)")
        }
    }
    task.resume()

Sử dụng bảo vệ, bạn có thể chuyển điều khiển ra khỏi phạm vi nếu một hoặc nhiều điều kiện không được đáp ứng.

let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

            /* GUARD: was there an error? */
            guard (error == nil) else {
                print("There was an error with your request: \(error)")
                return
            }

            /* GUARD: Did we get a successful 2XX response? */
            guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
                print("Your request returned a status code other than 2XX!")
                return
            }

            /* GUARD: was there any data returned? */
            guard let data = data else {
                print("No data was returned by the request!")
                return
            }

            //Process Data Here.
            print("Data: \(data)")
}
task.resume()

Tài liệu tham khảo:

1. Swift 2: Thoát sớm Với bảo vệ 2. Udacity 3. Tuyên Bố Guard


Nhưng bạn có thể làm sau với if statement? if condition { return }chắc chắn?
Oliver Dixon

2

Giống như một câu lệnh if, Guard bảo vệ các câu lệnh dựa trên giá trị Boolean của một biểu thức. Không giống như một câu lệnh if, các câu lệnh bảo vệ chỉ chạy nếu các điều kiện không được đáp ứng. Bạn có thể nghĩ về việc bảo vệ giống như một Assert, nhưng thay vì sụp đổ, bạn có thể thoát ra một cách duyên dáng.

tham khảo: http://ericcerney.com/swift-guard-statement/


1

Nó thực sự thực sự làm cho dòng chảy của một chuỗi với một số tra cứu và tùy chọn ngắn gọn và rõ ràng hơn nhiều và giảm rất nhiều nếu lồng nhau. Xem bài đăng của Erica Sadun về việc thay thế Ifs . .... Có thể được mang đi, một ví dụ dưới đây:

    let filteredLinks = locationsLinkedToList.filter({$0.actionVerb == movementCommand})
    guard let foundLink = filteredLinks.first else {return ("<Person> cannot go in that direction.", nil, nil)}
    guard filteredLinks.count == 1 else {return ("<Person> cannot decide which route to take.", nil, nil)}
    guard let nextLocation = foundLink.toLocation else {return ("<Person> cannot go in that direction.", nil, nil)}

Xem nếu gậy đó.


1

Nói một cách đơn giản, nó cung cấp một cách để xác nhận các trường trước khi thực hiện. Đây là một phong cách lập trình tốt vì nó tăng cường khả năng đọc. Trong các ngôn ngữ khác, nó có thể trông như thế này:

func doSomething() {
    if something == nil {
        // return, break, throw error, etc.
    }
    ...
}

Nhưng vì Swift cung cấp cho bạn các tùy chọn, chúng tôi không thể kiểm tra xem có phải không gán giá trị của nó cho một biến. Ngược lại, if letkiểm tra xem nó không phải là số không và gán một biến để giữ giá trị thực. Đây là nơi guardđi vào chơi. Nó cung cấp cho bạn một cách ngắn gọn hơn để thoát sớm bằng cách sử dụng tùy chọn.


1

Nguồn: Bảo vệ trong Swift

Hãy xem ví dụ để hiểu rõ về nó

Ví dụ 1:

func validate() {         
    guard 3>2 else {             
    print ("False")             
    return         
    }         
    print ("True") //True     
} 
validate()

Trong ví dụ trên, chúng ta thấy rằng 3 lớn hơn 2 và câu lệnh bên trong mệnh đề bảo vệ khác được bỏ qua và True được in.

Ví dụ 2:

func validate() {         
    guard 1>2 else {             
    print ("False")            //False 
    return         
    }         
    print ("True")      
} 
validate()

Trong ví dụ trên, chúng ta thấy rằng 1 nhỏ hơn 2 và câu lệnh bên trong mệnh đề bảo vệ khác được thực thi và Sai được in theo sau là return.

Example 3: gaurd let, unwrapping optionals through guard let

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          // Condition is false            return         
     }         
     print("Condition is met\(name)")     
} 
getName(args: "")

Trong ví dụ trên, chúng tôi đang sử dụng bảo vệ cho phép mở khóa các tùy chọn. Trong hàm getName, chúng tôi đã xác định một biến loại chuỗi myName là tùy chọn. Sau đó, chúng tôi sử dụng Guard let để kiểm tra xem biến myName có bằng không hay không, nếu không gán cho tên và kiểm tra lại, tên không trống. Nếu cả hai điều kiện đủ điều kiện tức là đúng thì khối khác sẽ bị bỏ qua và in Điều kiện được đáp ứng với tên tên.

Về cơ bản, chúng tôi đang kiểm tra hai thứ ở đây được phân tách bằng dấu phẩy, lần đầu tiên mở ra và tùy chọn và kiểm tra xem điều đó có thỏa mãn điều kiện hay không.

Ở đây chúng ta không chuyển gì cho hàm tức là chuỗi rỗng và do đó Điều kiện sai là in.

func getName(args myName: String?) {
     guard let name = myName, !name.isEmpty else {
     print ("Condition is false")          
     return         
     }        
     print("Condition is met \(name)") // Condition is met Hello    
} getName(args: "Hello")

Ở đây chúng tôi đang chuyển qua Hello Hello cho chức năng và bạn có thể thấy đầu ra được in Điều kiện được đáp ứng Hello Hello.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.