Các cách sử dụng AS, AS?, AS! một cách hiệu quả và an toàn trong code Swift

Chào các bạn, hôm nay chúng ta sẽ cùng nhau tìm hiểu rõ và chi tiết về các cách sử dụng các toán tử as, as?, as! trong Swift. Các toán tử này cũng là một trong những yếu tố tạo nên tính an toàn trong code Swift. Nếu bạn hiểu rõ nó thì code của bạn sẽ rất an toàn, nếu không thì có thể gây ra bug và crash ứng dụng triền miên.

Việc làm code Swift cho bạn chưa có nhiều kinh nghiệm

Để bắt đầu cho bài hướng dẫn này, các bạn nên sử dụng Xcode Playground để thực hành code theo các ví dụ trong bài, nó giúp bạn sẽ dễ hiểu và nhớ lâu hơn. Trong bài này có một vài kiến thức về optional nếu bạn nào chưa hiểu về nó thì có thể tìm hiểu tại đây.

Trước tiên chúng ta sẽ tìm hiểu về 2 ký tự “?”, “!”: Trong khai báo biến, ký tự “?” có nghĩa là ta nói rằng biến đó có khả năng bị nil chứ không phải lúc nào cũng có giá trị. Trong câu lệnh thông thường, dấu “?” xuất hiện báo hiệu rằng biến đó có thể bị nil và khi sử dụng biến đó mà biến đó bị nil thì app sẽ không bị crash. Trong khai báo biến, khi dùng ký tự “!” có nghĩa là chúng cực kỳ chắc chắn rằng biến có “!” sẽ luôn luôn có giá trị. Nếu không có giá trị trong các biến đó thì khi sử dụng biến đó sẽ gây ra crash ứng dụng.

Lưu ý: Khi chúng ta khai báo biến mà không biết biến này có giá trị hay không thì nên dùng ký tự “?” và ngược lại nếu biết biến luôn luôn có giá trị thì chúng ta sẽ dùng ký tự “!”. Ví dụ:

var a:Int?
 
print(a) // Hiện thị giá trị nil. Không carsh
 
 
let b:Int!
 
print(b) // Sẽ bị báo lỗi ngay lập tức.(Bị crash)
 
//Chúng ta có thể sửa như sau:
 
let b:Int! = 1
print(b)
 
//hoặc
 
var b:Int!
b = 1
print(b)

As sử dụng trong trường hợp chúng ta muốn ép kiểu(upcasting) dữ liệu nào đó thành một kiểu dữ liệu ta mong muốn. Lưu ý: Khi dùng As bạn phải chắc chắn một điều là kiểu dữ liệu bạn muốn ép kiểu cũng chính là kiểu dữ liệu của biến bạn đang muốn ép, nếu không sẽ xảy ra bug. Ví du:

// Ép kiểu (Up casting)
   let button = UIButton()
   let view = button as UIView
 
// Khái báo biến và ép kiểu
   let string = "3"
   let nsString = string as NSString
   let value = nsString.floatValue // 3
 
   let rawString: AnyObject = "Tự học Swift"
   let optionalString: AnyObject? = "Tự học Swift"
   let nilString: AnyObject? = (nil as String?)
 
   let rawInt: AnyObject = Int(3)
   let optionalInt: AnyObject? = Int(3)
   let nilInt: AnyObject? = (nil as Int?)
 
// Sử dụng as
   let result1 = rawString as String       // AnyObject is not convertible to String
   let result2 = optionalString as String  // AnyObject? is not convertible to String
   let result3 = nilString as String       // AnyObject? is not convertible to String
   let result4 = rawInt as String          // AnyObject is not convertible to String
   let result5 = optionalInt as String     // AnyObject? is not convertible to String
   let result6 = nilInt as String          // AnyObject? is not convertible to String
Tuy nhiên chúng ta có thể sử dụng cách ép kiểu an toàn bằng cách dùng toán tử sau: As? Được dùng giống như As nhưng khác biệt ở chỗ là nếu trường hợp chúng ta ép kiểu không thành công nó sẽ trả về trị nil. Để hiểu rõ hơn chúng ta xem ví dụ sau: Ví dụ:
let string = "3"
   let nsString = string as NSString
   let value = nsString.floatValue // 3
 
   let rawString: AnyObject = "Tự học Swift"
   let optionalString: AnyObject? = "Tự học Swift"
   let nilString: AnyObject? = (nil as String?)
 
   let rawInt: AnyObject = Int(3)
   let optionalInt: AnyObject? = Int(3)
   let nilInt: AnyObject? = (nil as Int?)
 
// as?
   let result17 = rawString as? String // String? "Tự học Swift"
   let result18 = optionalString as? String // String? "Tự học Swift"
   let result19 = nilString as? String // String? nil
   let result20 = rawInt as? String // String? nil
   let result21 = optionalInt as? String // String? nil
   let result22 = nilInt as? String // String? nil
 
   let result23 = rawString as? String! // Không thể ép kiểu từ AnyObject sang kiểu optional String!
   let result24 = rawString as? String? // Không thể ép kiểu từ AnyObject sang kiểu optional String?
   let result25 = optionalString as? String! // String!? "Tự học Swift"
   let result26 = optionalString as? String? // String?? "Tự học Swift
   let result27 = optionalInt as? String? // String?? nil

As! Được dùng Giống như As vậy tuy nhiên nó sẽ bắt buộc biến của chúng ta muốn ép kiểu phải trở thành kiểu dữ liệu nào đó. Nếu trường hợp biến đó không thể ép về kiểu đó thì sẽ xảy ra crash ứng dụng ngay. Lưu ý: Sử dụng thèn này rất nguy hiểm, nếu không chắc chắn được rằng kiểu chúng ta muốn ép sẽ thành công thì sẽ gây ra crash ứng dụng. Nếu chúng ta cảm thấy không chắc chắn hay mơ hồ về việc ép kiểu dư liệu này thì chúng ta nên dùng as? Để bắt trường hợp không ép thành công và xử lý tiếp. Ví dụ:

if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
    //Nếu chúng ta ép kiểu thành công thì sử dụng biến cell để làm gì đó
} else {
    // Ép không thành công thì chúng ta xử lý gì đó
}

Ví dụ:

let string = "3"
   let nsString = string as NSString
   let value = nsString.floatValue // 3
 
   let rawString: AnyObject = "Tự học Swift"
   let optionalString: AnyObject? = "Tự học Swift"
   let nilString: AnyObject? = (nil as String?)
 
   let rawInt: AnyObject = Int(3)
   let optionalInt: AnyObject? = Int(3)
   let nilInt: AnyObject? = (nil as Int?)
 
// as!
   let result7 = rawString as! String // String "Tự học Swift"
   let result8 = optionalString as! String // String "Tự học Swift"
//    let result9 = nilString as! String // unexpectedly found nil while unwrapping an Optional value
//    let result10 = rawInt as! String // Could not cast value of type '__NSCFNumber' (0x107c98130) to 'NSString' (0x1082afb00).
//    let result11 = optionalInt as! String // Could not cast value of type '__NSCFNumber' (0x101e95130) to 'NSString' (0x1024acb00).
//    let result12 = nilInt as! String // unexpectedly found nil while unwrapping an Optional value
 
 //    let result13 = rawString as! String! // Không thể ép kiểu từ AnyObject thành kiểu optional String!
 //    let result14 = rawString as! String? //Không thể ép từ kiểu AnyObject thành kiểu optional  String?
   let result15 = optionalString as! String! // String! "Tự học Swift"
   let result16 = optionalString as! String? // String? "Tự học Swift"

Lưu ý:As! Cũng không thể ép kiểu dữ liệu nào đó thành kiểu optionalAs? Và As! Ngoài việc ép kiểu để tạo ra biến mới và sử dụng nó thì chúng còn có công dụng để kiểu tra xem biến đó có chính xác thuộc loại kiệu dữ liệu mà mình muốn kiểm tra hay không. Nếu bạn ép kiểu thành công với as? Thì nó sẽ cho bạn một biến mới kiểu optional, ngược lại với as!, khi bạn ép kiểu thành công nó sẽ cho ra một biến bình thường. Để hiểu hơn chúng ta sẽ tham khảo ví dụ bên dưới:

Ví dụ 1:

var optionalString = dict.objectForKey(“SomeKey”) as? String

optionalString Sẽ là một biến optional kiểu String?, Nếu trường hợp chúng ta không ép kiểu thành kiểu dữ liệu String, có nghĩa là sẽ có trường hợp nào đó mà giá trị của dict.objectForKey(“SomeKey”) là một kiểu dữ liệu nào đó khác String thì khi đó dùng as? String sẽ ép kiểu không thành công thì biến optionalString sẽ được gián nil.

Ví dụ 2:

var optionalString = dict.objectForKey(“SomeKey”) as! String?

Trường hợp này thì bạn nói với hệ thống là kiểu dữ liệu của dict.objectForKey(“SomeKey”) là kiểu String?, chắc chắc là kiểu String?. Nếu khẳng định trên của bạn là đúng thì chúng ta sẽ có một biến optionalString  với kiểu String? Nhưng không may nếu khẳng định của bạn là sai, có nghĩa là dict.objectForKey(“SomeKey”) không phải là kiểu String?, thì sẽ xảy ra crash ngay lập tức. Vậy giải pháp ở đây là chúng ta sẽ sử dụng cú pháp if let hay guard let… để kiểm tra kiểu dữ liệu của biến đó, nếu ta không chắc chắn. Ví dụ:

if let string = dict.objectForKey("SomeKey") as? String { 
  //Sử dụng biến đó
  println(string) 
} else {
  // Làm gì đó nếu biến đó không phải kiểu String
}
Guard let string = dict.objectForKey("SomeKey") as? String else { 
  // Làm gì đó nếu biến đó không phải kiểu String
return
} 
//Sử dụng biến đó
println(string)

Kết thúc phần này tuy không mấy khó hiểu nhưng nếu các bạn thực sự không nắm chắc và hiểu rõ về các từ khóa as, as?, as! thì khi sử dụng có thể gây ra crash ứng dụng một cách vô lý. Nếu bạn nào chưa rõ nên thực hành các ví dụ bên trên với background để hiểu và nhớ lâu hơn các trường hợp nên dùng và không nên dùng các từ khóa trên. Hy vọng các bạn thích và học được nhiều kiến thức từ bài viết này. Mong các bạn chia sẽ nó để mọi người cùng học và cùng trao đổi. Mọi thắc mắc hay trao đổi về bài viết, các bạn có thể để lại bình luận bên dưới mình sẽ hỗ trợ sớm nhất.
Chân thành cảm ơn các bạn đã theo dõi.

Nguồn gốc bài viết từ CafeDev