Swift: bảo vệ let vs if let


129

Tôi đã đọc về Tùy chọn trong Swift và tôi đã thấy các ví dụ if letđược sử dụng để kiểm tra xem Tùy chọn có giữ giá trị hay không và trong trường hợp đó - hãy làm gì đó với giá trị chưa được đặt.

Tuy nhiên, tôi đã thấy rằng trong Swift 2.0, từ khóa guard letđược sử dụng chủ yếu. Tôi tự hỏi liệu if letđã được gỡ bỏ khỏi Swift 2.0 hay liệu nó vẫn có thể được sử dụng.

Tôi có nên thay đổi chương trình của tôi có chứa if lettới guard let?

Câu trả lời:


163

if letguard letphục vụ các mục đích tương tự, nhưng khác biệt.

Trường hợp "khác" guardphải thoát khỏi phạm vi hiện tại. Nói chung có nghĩa là nó phải gọi returnhoặc hủy bỏ chương trình. guardđược sử dụng để cung cấp trả về sớm mà không yêu cầu lồng phần còn lại của hàm.

if letlồng phạm vi của nó, và không yêu cầu bất cứ điều gì đặc biệt của nó. Nó có thể returnhoặc không.

Nói chung, nếu if-letkhối sẽ là phần còn lại của hàm, hoặc elsemệnh đề của nó sẽ có returnhoặc hủy bỏ trong đó, thì bạn nên sử dụng guardthay thế. Điều này thường có nghĩa (ít nhất là theo kinh nghiệm của tôi), khi nghi ngờ, guardthường là câu trả lời tốt hơn. Nhưng có rất nhiều tình huống if letvẫn còn phù hợp.


37
Sử dụng if letkhi non-niltrường hợp hợp lệ. Sử dụng guardkhi niltrường hợp đại diện cho một số loại lỗi.
BallpointBen

4
@BallpointBen Tôi không đồng ý với điều đó. Có nhiều trường hợp guardphù hợp ngay cả khi không có lỗi. Đôi khi nó chỉ có nghĩa là không có gì để làm. Ví dụ, một positionTitlephương pháp có thể guard if let title = title else {return}. Tiêu đề có thể là tùy chọn, trong trường hợp này không phải là một lỗi. Nhưng guard letvẫn thích hợp.
Rob Napier

1
Vâng; Tôi có nghĩa là bảo vệ cho vào bình luận.
Rob Napier

1
nói cách khác, "Guard let" được sử dụng khi mã chắc chắn 99% không sử dụng điều kiện khác; mặt khác, "nếu cho phép" khi mã là 50 - 50 (ví dụ) để sử dụng điều kiện khác.
Chino Pan

1
Các biến bị ràng buộc bởi if letchỉ có thể nhìn thấy bên trong if let phạm vi. Các biến bị ràng buộc bởi guard letcó thể nhìn thấy sau đó. Vì vậy, nó có ý nghĩa để sử dụng bảo vệ để ràng buộc các giá trị tùy chọn cũng.
Boweidmann

103

Bảo vệ có thể cải thiện rõ ràng

Khi bạn sử dụng bảo vệ, bạn có một kỳ vọng cao hơn nhiều để người bảo vệ thành công và điều quan trọng là nếu nó không thành công, thì bạn chỉ muốn thoát khỏi phạm vi sớm . Giống như bạn bảo vệ để xem nếu một tập tin / hình ảnh tồn tại, nếu một mảng làEmpty hay không.

func icon() -> UIImage {
    guard let image = UIImage(named: "Photo") else {
        return UIImage(named: "Default")! //This is your fallback
    }
    return image //-----------------you're always expecting/hoping this to happen
}

Nếu bạn viết mã ở trên với if-hãy để nó truyền đạt cho nhà phát triển đọc rằng nó nhiều hơn 50-50. Nhưng nếu bạn sử dụng bảo vệ, bạn thêm sự rõ ràng vào mã của mình và nó ngụ ý rằng tôi hy vọng điều này sẽ hoạt động 95% thời gian ... nếu nó thất bại, tôi không biết tại sao nó lại xảy ra; điều đó rất khó xảy ra ... nhưng sau đó chỉ sử dụng hình ảnh mặc định này hoặc có lẽ chỉ cần xác nhận bằng một thông điệp có ý nghĩa mô tả những gì đã sai!

  • Tránh guards khi chúng tạo ra tác dụng phụ, các vệ sĩ sẽ được sử dụng như một dòng chảy tự nhiên . Tránh bảo vệ khi elsemệnh đề giới thiệu tác dụng phụ. Bảo vệ thiết lập các điều kiện cần thiết để mã thực thi đúng, cung cấp thoát sớm

  • Khi bạn thực hiện tính toán đáng kể trong nhánh tích cực, hãy cấu trúc lại từ ifmột guardcâu lệnh và trả về giá trị dự phòng trong elsemệnh đề

Từ: Cuốn sách Phong cách Swift của Erica Sadun

Ngoài ra, là kết quả của các đề xuất và mã sạch ở trên, nhiều khả năng bạn sẽ muốn / cần thêm các xác nhận vào các tuyên bố bảo vệ thất bại , nó chỉ cải thiện khả năng đọc và làm rõ cho các nhà phát triển khác những gì bạn đang mong đợi.

guard​ ​let​ image =UIImage(named: selectedImageName) else { // YESSSSSS
     assertionFailure("Missing ​​\(​selectedImageName​)​​ asset") 
     return
} 

guard​ ​let​ image =UIImage(named: selectedImageName) else { // NOOOOOOO
​     ​return 
}

Từ: Cuốn sách Phong cách Swift của Erica Sadun + một số sửa đổi

(bạn sẽ không sử dụng các xác nhận / điều kiện tiên quyết cho if-lets. Nó dường như không đúng)

Sử dụng vệ sĩ cũng giúp bạn cải thiện sự rõ ràng bằng cách tránh kim tự tháp diệt vong. Xem câu trả lời của Nitin .


Bảo vệ tạo ra một biến mới

Có một sự khác biệt quan trọng mà tôi tin rằng không ai giải thích rõ.

Cả hai guard letif let Unwrap biến tuy nhiên

Với guard letbạn đang tạo một biến mới sẽ tồn tại bên ngoàielse câu lệnh.

Với việc if letbạn không tạo bất kỳ biến mới nào sau câu lệnh khác, bạn chỉ nhập khối mã nếu tùy chọn không phải là không. Biến mới được tạo chỉ tồn tại bên trong khối mã không sau!

guard let:

func someFunc(blog: String?) {

    guard let blogName = blog else {
        print("some ErrorMessage")
        print(blogName) // will create an error Because blogName isn't defined yet
        return
    }
    print(blogName) // You can access it here ie AFTER the guard statement!!

    //And if I decided to do 'another' guard let with the same name ie 'blogName' then I would create an error!
    guard let blogName = blog else { // errorLine: Definition Conflicts with previous value.
        print(" Some errorMessage")
        return
    }
    print(blogName)
}

if-let:

func someFunc(blog: String?) {


    if let blogName1 = blog {
        print(blogName1) // You can only access it inside the code block. Outside code block it doesn't exist!
    }
    if let blogName1 = blog { // No Error at this line! Because blogName only exists inside the code block ie {}
        print(blogName1)
    }
}

Để biết thêm thông tin về if lethãy xem: Tại sao việc khai báo lại ràng buộc tùy chọn không tạo ra lỗi


Bảo vệ yêu cầu thoát khỏi phạm vi

(Cũng được đề cập trong câu trả lời của Rob Napier):

Bạn PHẢI đã guardxác định bên trong một func. Mục đích chính của nó là hủy bỏ phạm vi / trả lại / thoát, nếu một điều kiện không được đáp ứng:

var str : String?

guard let blogName1 = str else {
    print("some error")
    return // Error: Return invalid outside of a func
}
print (blogName1)

Đối với if letbạn không cần phải có nó trong bất kỳ func:

var str : String?    
if let blogName1 = str {
   print(blogName1) // You don't get any errors!
}

guard đấu với if

Nó đáng chú ý là nó thích hợp hơn để xem câu hỏi này như guard letvs if letguardvs if.

Một độc lập ifkhông làm bất kỳ điều gì, cũng không độc lập guard. Xem ví dụ dưới đây. Nó không thoát sớm nếu giá trị là nil. KHÔNG có giá trị tùy chọn. Nó chỉ thoát sớm nếu một điều kiện không được đáp ứng.

let array = ["a", "b", "c"]
func subscript(at index: Int) -> String?{
   guard index > 0, index < array.count  else { return nil} // exit early with bad index
   return array[index]
}

45

Khi nào nên sử dụng if-letvà khi nào nên sử dụng guardthường là một câu hỏi về phong cách.

Giả sử bạn có func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Intvà một mảng tùy chọn của các mục ( var optionalArray: [SomeType]?) và bạn cần trả về 0nếu mảng là nil(không được đặt) hoặc countnếu mảng có giá trị (được đặt).

Bạn có thể thực hiện nó như thế này bằng cách sử dụng if-let:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        if let array = optionalArray {
            return array.count
        }
        return 0
    }

hoặc như thế này bằng cách sử dụng guard:

func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
    {
        guard let array = optionalArray else {
            return 0
        }
        return array.count
    }

Các ví dụ giống nhau về chức năng.

Ở đâu guard thực sự tỏa sáng là khi bạn có một nhiệm vụ như xác nhận dữ liệu và bạn muốn chức năng bị lỗi sớm nếu có gì sai.

Thay vì lồng một loạt các if-letkhi bạn tiến gần đến việc hoàn tất xác nhận, "đường dẫn thành công" và các tùy chọn ràng buộc thành công hiện tại đều nằm trong phạm vi chính của phương thức, vì tất cả các đường dẫn thất bại đã quay trở lại.


29

Tôi sẽ cố gắng giải thích sự hữu ích của các tuyên bố bảo vệ với một số mã (không được tối ưu hóa).

Bạn có một giao diện người dùng nơi bạn đang xác thực các trường văn bản để đăng ký người dùng với tên, họ, email, điện thoại và mật khẩu.

Nếu bất kỳ textField nào không chứa văn bản hợp lệ, nó sẽ làm cho trường đó trở thành FirstResponder.

đây là mã không được tối ưu hóa:

//pyramid of doom

func validateFieldsAndContinueRegistration() {
    if let firstNameString = firstName.text where firstNameString.characters.count > 0{
        if let lastNameString = lastName.text where lastNameString.characters.count > 0{
            if let emailString = email.text where emailString.characters.count > 3 && emailString.containsString("@") && emailString.containsString(".") {
                if let passwordString = password.text where passwordString.characters.count > 7{
                    // all text fields have valid text
                    let accountModel = AccountModel()
                    accountModel.firstName = firstNameString
                    accountModel.lastName = lastNameString
                    accountModel.email = emailString
                    accountModel.password = passwordString
                    APIHandler.sharedInstance.registerUser(accountModel)
                } else {
                    password.becomeFirstResponder()
                }
            } else {
                email.becomeFirstResponder()
            }
        } else {
            lastName.becomeFirstResponder()
        }
    } else {
        firstName.becomeFirstResponder()
    }
}

Bạn có thể thấy ở trên, rằng tất cả các chuỗi (FirstNameString, lastNameString, v.v.) chỉ có thể truy cập trong phạm vi của câu lệnh if. do đó, nó tạo ra "kim tự tháp diệt vong" này và có nhiều vấn đề với nó, bao gồm khả năng đọc và dễ dàng di chuyển mọi thứ (nếu thứ tự của các trường bị thay đổi, bạn phải viết lại hầu hết mã này)

Với câu lệnh bảo vệ (trong mã bên dưới), bạn có thể thấy các chuỗi này có sẵn bên ngoài {}và được sử dụng, nếu tất cả các trường là hợp lệ.

// guard let no pyramid of doom
func validateFieldsAndContinueRegistration() {

guard let firstNameString = firstName.text where firstNameString.characters.count > 0 else {
            firstName.becomeFirstResponder()
            return
        }
guard let lastNameString = lastName.text where lastNameString.characters.count > 0 else {
            lastName.becomeFirstResponder()
            return
        }
guard let emailString = email.text where 
        emailString.characters.count > 3 &&
        emailString.containsString("@") && 
        emailString.containsString(".") else {
            email.becomeFirstResponder()
            return
        }
guard let passwordString = password.text where passwordString.characters.count > 7 else {
            password.becomeFirstResponder()
            return
        }

// all text fields have valid text
    let accountModel = AccountModel()
    accountModel.firstName = firstNameString
    accountModel.lastName = lastNameString
    accountModel.email = emailString
    accountModel.password = passwordString
    APIHandler.sharedInstance.registerUser(accountModel)
}

Nếu thứ tự của các trường thay đổi, chỉ cần di chuyển các dòng mã tương ứng lên hoặc xuống, và bạn tốt để đi.

Đây là một lời giải thích rất đơn giản và một trường hợp sử dụng. Hi vọng điêu nay co ich!


13

Sự khác biệt cơ bản

Bảo vệ hãy để

  1. Quá trình tồn tại sớm từ phạm vi
  2. Yêu cầu điểm số hiện có như trả lại, Ném, vv
  3. Tạo một biến mới có thể được truy cập ngoài phạm vi.

nếu để

  1. Không thể truy cập ra phạm vi.
  2. không cần phải trả lại tuyên bố. Nhưng chúng ta có thể viết

LƯU Ý: Cả hai đều được sử dụng để mở khóa biến Tùy chọn.



2

bảo vệ

  • Một guardcâu lệnh đượ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.

  • Giá trị của bất kỳ điều kiện nào trong guardcâu lệnh phải là kiểu Bool hoặc kiểu được bắc cầu đến Bool. Điều kiện cũng có thể là một tuyên bố ràng buộc tùy chọn.

Một tuyên bố bảo vệ có hình thức sau đây:

guard condition else {
    //Generally return
}

nếu để

  • Cũng phổ biến như ràng buộc tùy chọn .
  • Để truy cập đối tượng tùy chọn, chúng tôi sử dụng if let.
if let roomCount = optionalValue {
    print("roomCount available")
} else {
    print("roomCount is nil")
}

1

Tôi đã học được điều này từ swift với Bob ..

Khác-Nếu điển hình

 func checkDrinkingAge() {
      let canDrink = true

     if canDrink {
        print("You may enter")
       // More Code
        // More Code
      // More Code

         } else {
         // More Code
    // More Code
    // More Code
    print("Let me take you to the jail")
          }
     }

Các vấn đề với Else-If

  1. Dấu ngoặc lồng
  2. Phải đọc từng dòng để phát hiện thông báo lỗi

Tuyên bố bảo vệ Một khối bảo vệ chỉ chạy nếu điều kiện sai và nó sẽ thoát khỏi chức năng thông qua trả về. Nếu điều kiện là đúng, Swift bỏ qua khối bảo vệ. Nó cung cấp một lối thoát sớm và ít dấu ngoặc.

func checkDrinkProgram() {
       let iCanDrink = true

           guard iCanDrink else {
        // if iCanDrink == false, run this block
         print("Let's me take you to the jail")
          return
        }

         print("You may drink")
           // You may move on
                  // Come on.
                 // You may leave
                // You don't need to read this.
                 // Only one bracket on the bottom: feeling zen.
       }

Tùy chọn Unwrap với Else-If

Một tuyên bố bảo vệ không chỉ hữu ích để thay thế một khối có điều kiện thông thường bằng một câu lệnh if-if khác, mà còn tuyệt vời cho các tùy chọn hủy ghép bằng cách giảm thiểu số lượng dấu ngoặc. Để so sánh, trước tiên hãy bắt đầu làm thế nào để hủy ghép nhiều tùy chọn với khác-if. Đầu tiên, chúng ta hãy tạo ba tùy chọn sẽ được mở ra.

var publicName: String? = "Bob Lee"
var publicPhoto: String? = "Bob's Face"
var publicAge: Int? = nil

Cơn ác mộng tồi tệ nhất

func unwrapOneByOne() {
         if let name = publicName {
              if let photo = publicPhoto {
                     if let age = publicAge {
                        print("Bob: \(name), \(photo), \(age)")
                                  } else {
                          print("age is mising")
                           }
                  } else {
                      print("photo is missing")
                         }
                  } else {
                        print("name is missing")
                         }
                  }

Mã ở trên chắc chắn hoạt động nhưng vi phạm nguyên tắc DRY. Thật là tàn bạo. Hãy để chúng tôi phá vỡ nó. +

Tốt hơn một chút Mã bên dưới dễ đọc hơn ở trên. +

func unwrapBetter() {
         if let name = publicName {
       print("Yes name")
                   } else {
               print("No name")
        return
      }

         if let photo = publicPhoto {
             print("Yes photo")
            } else {
           print("No photo")
       return
             }

        if let age = publicAge {
            print("Yes age")
                      } else {
                print("No age")
            return
                           }
     }

Unwrap with Guard Các câu lệnh if-if khác có thể được thay thế bằng Guard. +

 func unwrapOneByOneWithGuard() {
             guard let name = publicName else {
                  print("Name missing")
              return
                                        }

              guard let photo = publicPhoto else {
              print("Photo missing")
                return
                                            }

                  guard let age = publicAge else {
                   print("Age missing")
                                     return
                                                 }
                 print(name)
                 print(photo)
                 print(age)
         }

Unwrap Nhiều tùy chọn với Else - Nếu cho đến nay, bạn đã hủy kết nối từng tùy chọn một. Swift cho phép chúng ta mở khóa nhiều tùy chọn cùng một lúc. Nếu một trong số chúng chứa nil, nó sẽ thực thi khối khác.

func unwrap() {
  if let name = publicName, let photo = publicPhoto, let age = publicAge {
    print("Your name is \(name). I see your face right here, \(photo), you are \(age)")
  } else {
    // if any one of those is missing
    print("Something is missing")
  }
}

Xin lưu ý rằng khi bạn hủy ghép nhiều tùy chọn cùng một lúc, bạn không thể xác định được cái nào chứa con số không

Unwrap Nhiều tùy chọn với Guard Tất nhiên, chúng ta nên sử dụng bảo vệ khác-if. +

func unwrapWithGuard() {
  guard let name = publicName, let photo = publicPhoto, let age = publicAge else {
    // if one or two of the variables contain "nil"
    print("Something is missing")
    return
  }

  print("Your name is \(name). I see your, \(photo). You are \(age).")
  // Animation Logic
  // Networking
  // More Code, but still zen
}

vui lòng quay lại và sửa định dạng / thụt mã của bạn!
pkamb
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.