Hạ cấp các tùy chọn trong Swift: as? Nhập, hoặc dưới dạng! Kiểu?


Câu trả lời:


142

Sự khác biệt thực tế là:

var optionalString = dict["SomeKey"] as? String

optionalStringsẽ là một biến kiểu String?. Nếu kiểu cơ bản là một thứ gì đó khác với kiểu Stringnày, điều này sẽ vô hại chỉ gán nilcho tùy chọn.

var optionalString = dict["SomeKey"] as! String?

Điều này nói rằng, tôi biết điều này là một String?. Điều này cũng sẽ dẫn đến optionalStringmột loại String?, nhưng nó sẽ sụp đổ nếu loại cơ bản là một cái gì đó khác.

Sau đó, kiểu đầu tiên được sử dụng với if letđể mở gói tùy chọn một cách an toàn:

if let string = dict["SomeKey"] as? String {
    // If I get here, I know that "SomeKey" is a valid key in the dictionary, I correctly
    // identified the type as String, and the value is now unwrapped and ready to use.  In
    // this case "string" has the type "String".
    print(string)
}

Không phải phương pháp đầu tiên luôn tốt hơn? Cả hai đều trả về một tùy chọn của loại Chuỗi? Có vẻ như phương pháp thứ hai thực hiện tương tự như phương pháp đầu tiên nhưng có thể gặp sự cố nếu quá trình downcast không thành công. Vậy tại sao lại sử dụng nó?
Sikander

6
Có @Sikander, cái đầu tiên luôn tốt hơn. Tôi sẽ không bao giờ sử dụng thứ hai.
vacawama

14

as? Types- có nghĩa là quá trình truyền xuống là tùy chọn. Quá trình có thể thành công hoặc không (hệ thống sẽ trả về nil nếu quá trình truyền xuống không thành công). Bất kỳ cách nào sẽ không bị sập nếu quá trình truyền xuống không thành công.

as! Type?- Ở đây quá trình ép xuống sẽ thành công ( !chỉ ra rằng). Dấu chấm hỏi kết thúc cho biết liệu kết quả cuối cùng có thể là con số không hay không.

Thông tin thêm về "!" và "?"

Hãy để chúng tôi lấy 2 trường hợp

  1. Xem xét:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell

    Ở đây chúng tôi không biết liệu kết quả của việc truyền xuống ô có mã định danh "Cell" thành UITableViewCell có thành công hay không. Nếu không thành công thì nó trả về nil (vì vậy chúng tôi tránh sự cố ở đây). Ở đây chúng tôi có thể làm như được đưa ra dưới đây.

    if let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as? UITableViewCell {
        // If we reached here it means the down casting was successful
    }
    else {
        // unsuccessful down casting
    }

    Vì vậy, chúng ta hãy ghi nhớ nó như thế này - Nếu ?điều đó có nghĩa là chúng ta không chắc liệu giá trị có phải là nil hay không (dấu hỏi xuất hiện khi chúng ta không biết mọi thứ).

  2. Đối chiếu điều đó với:

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell") as! UITableViewCell. 

    Ở đây chúng tôi nói với trình biên dịch rằng quá trình truyền xuống sẽ thành công. Nếu nó không thành công hệ thống sẽ sụp đổ. Vì vậy, chúng tôi đưa ra !khi chúng tôi chắc chắn rằng giá trị đó không phải là nil.


11

Để làm rõ những gì vacawama đã nói, đây là một ví dụ ...

Swift 3.0:

import UIKit

let str_value:    Any   = String("abc")!
let strOpt_value: Any?  = String("abc")!
let strOpt_nil:   Any?  = (nil as String?)
let int_value:    Any   = Int(1)
let intOpt_value: Any?  = Int(1)
let intOpt_nil:   Any?  = (nil as Int?)

// as String
//str_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//int_value     as String // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_value  as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// as? String
  str_value     as? String // == "abc"
  strOpt_value  as? String // == "abc"
  strOpt_nil    as? String // == nil
  int_value     as? String // == nil
  intOpt_value  as? String // == nil
  intOpt_nil    as? String // == nil

// as! String
  str_value     as! String // == "abc"
  strOpt_value  as! String // == "abc"
//strOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.
//int_value     as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_value  as! String // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
//intOpt_nil    as! String // Run-Time Error: unexpectedly found nil while unwrapping an Optional value.

// as String?
//str_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//strOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//strOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//int_value     as String? // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//intOpt_value  as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//intOpt_nil    as String? // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// as? String?
//str_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as? String? // == "abc"
  strOpt_nil    as? String? // == nil
//int_value     as? String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  intOpt_value  as? String? // == nil
  intOpt_nil    as? String? // == nil

// as! String?
//str_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  strOpt_value  as! String? // == "abc"
  strOpt_nil    as! String? // == nil
//int_value     as! String? // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//intOpt_value  as! String? // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  intOpt_nil    as! String? // == nil

// let _ = ... as String
//if let _ = str_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String { true } // Compile-Time Error: 'Any' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_value as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String { true } // Compile-Time Error: 'Any?' is not convertible to 'String'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String
if let _ = str_value    as? String { true } // true
if let _ = strOpt_value as? String { true } // true
if let _ = strOpt_nil   as? String { true } // false
if let _ = int_value    as? String { true } // false
if let _ = intOpt_value as? String { true } // false
if let _ = intOpt_nil   as? String { true } // false

// let _ = ... as! String
//if let _ = str_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = strOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = int_value    as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_value as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'
//if let _ = intOpt_nil   as! String { true } // Compile-Time Error: initializer for conditional binding must have Optional type, not 'String'

// let _ = ... as String?
//if let _ = str_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = strOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = strOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = int_value    as String? { true } // Compile-Time Error: cannot convert value of type 'Any' to type 'String?' in coercion
//if let _ = intOpt_value as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?
//if let _ = intOpt_nil   as String? { true } // Compile-Time Error: 'Any?' is not convertible to 'String?'; did you mean to use 'as!' to force downcast?

// let _ = ... as? String?
//if let _ = str_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as? String? { true } // true
  if let _ = strOpt_nil   as? String? { true } // true
//if let _ = int_value    as? String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = intOpt_value as? String? { true } // false
  if let _ = intOpt_nil   as? String? { true } // true

// let _ = ... as! String?
//if let _ = str_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
  if let _ = strOpt_value as! String? { true } // true
  if let _ = strOpt_nil   as! String? { true } // false
//if let _ = int_value    as! String? { true } // Compile-Time Error: cannot downcast from 'Any' to a more optional type 'String?'
//if let _ = intOpt_value as! String? { true } // Run-Time Error: Could not cast value of type 'Swift.Int' to 'Swift.String'.
  if let _ = intOpt_nil   as! String? { true } // false

Swift 2.0:

import UIKit

let str:    AnyObject   = String("abc")
let strOpt: AnyObject?  = String("abc")
let strNil: AnyObject?  = (nil as String?)
let int:    AnyObject   = Int(1)
let intOpt: AnyObject?  = Int(1)
let intNil: AnyObject?  = (nil as Int?)

str    as? String // == "abc"
strOpt as? String // == "abc"
strNil as? String // == nil
int    as? String // == nil
intOpt as? String // == nil
intNil as? String // == nil

str    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
strOpt as! String? // == "abc"
strNil as! String? // == nil
int    as! String? // Compile-Time Error: Cannot downcast from 'AnyObject' to a more optional type 'String?'
intOpt as! String? // Run-Time Error: Could not cast value of type '__NSCFNumber' to 'NSString'
intNil as! String? // == nil

+1 cho ví dụ của bạn nhưng bạn có thể giải thích cho tôi cùng một ví dụ để sử dụng như! thay cho? trong khi downcasting dưới dạng let cell = tableView.dequeueReusableCellWithIdentifier ("Cell") dưới dạng! UITableViewCell..tôi đoán là? đã đủ tại sao lại cần như!
Anish Parajuli 웃

hãy để ô = tableView.dequeueReusableCellWithIdentifier ("Ô") là? UITableViewCell. - ở đây chúng tôi không biết liệu kết quả của việc truyền xuống ô có định danh "Cell" thành UITableViewCell có phải là nill hay không. Nếu nill thì nó trả về nill (vì vậy chúng tôi tránh sự cố ở đây).
jishnu bala

thú vị, intNil as! String? // ==nilkhông gây ra sự cố !!! ???, vì <Int> Tùy chọn. Không có gì khác với <Chuỗi> Tùy chọn. Không có
onmyway133

tại sao bạn as?lại thất vọng String? Tại sao bạn không từ chối nó String?? Tại sao bạn không nhìn xuống as!để String?
Honey

Đang cố gắng thực hiện sân chơi này trong Swift 3, nhưng bạn phải sử dụng Anythay vìAnyObject
Honey

9
  • as được sử dụng để dự báo và truyền kiểu sang kiểu bắc cầu
  • as? được sử dụng để truyền an toàn, trả về nil nếu không thành công
  • as! được sử dụng để ép đúc, sụp đổ nếu không thành công

Ghi chú:

  • as! không thể truyền loại thô thành tùy chọn

Ví dụ:

let rawString: AnyObject = "I love swift"
let optionalString: AnyObject? = "we love swift"
let nilString: AnyObject? = (nil as String?)

let rawInt: AnyObject = Int(3)
let optionalInt: AnyObject? = Int(3)
let nilInt: AnyObject? = (nil as Int?)

Thí dụ

var age: Int? = nil
var height: Int? = 180

Bằng cách thêm một ? ngay sau kiểu dữ liệu, bạn cho trình biên dịch biết rằng biến đó có thể chứa một số hoặc không. Khéo léo! Lưu ý rằng không thực sự có ý nghĩa khi xác định các hằng số Tùy chọn - bạn chỉ có thể đặt giá trị của chúng một lần và do đó bạn có thể nói liệu giá trị của chúng có bằng không hay không.

Khi nào chúng ta nên sử dụng "?" và khi "!"

giả sử chúng tôi có ứng dụng đơn giản dựa trên UIKit. chúng tôi có một số mã trong bộ điều khiển chế độ xem của mình và muốn trình bày bộ điều khiển chế độ xem mới ở trên nó. và chúng tôi cần quyết định đẩy chế độ xem mới trên màn hình bằng bộ điều khiển điều hướng.

Như chúng ta biết mọi cá thể ViewController đều có bộ điều khiển điều hướng thuộc tính. Nếu bạn đang xây dựng ứng dụng dựa trên bộ điều khiển điều hướng, thuộc tính này của bộ điều khiển chế độ xem chính của ứng dụng được đặt tự động và bạn có thể sử dụng nó để đẩy hoặc bật bộ điều khiển chế độ xem. Nếu bạn sử dụng một mẫu dự án ứng dụng duy nhất - sẽ không có bộ điều khiển điều hướng được tạo tự động cho bạn, vì vậy bộ điều khiển chế độ xem mặc định của ứng dụng sẽ không có bất kỳ thứ gì được lưu trữ trong thuộc tính navigationController.

Tôi chắc rằng bạn đã đoán rằng đây chính xác là một trường hợp cho một kiểu dữ liệu Tùy chọn. Nếu bạn kiểm tra UIViewController, bạn sẽ thấy rằng thuộc tính được định nghĩa là:

var navigationController: UINavigationController? { get }

Vì vậy, hãy quay lại trường hợp sử dụng của chúng ta. Nếu bạn biết thực tế rằng bộ điều khiển chế độ xem của mình sẽ luôn có bộ điều khiển điều hướng, bạn có thể tiếp tục và buộc mở nó ra:

controller.navigationController!.pushViewController(myViewController, animated: true)

Khi bạn đặt một! đằng sau tên thuộc tính mà bạn nói với trình biên dịch Tôi không quan tâm rằng thuộc tính này là tùy chọn, tôi biết rằng khi mã này thực thi sẽ luôn có một kho lưu trữ giá trị vì vậy hãy coi tùy chọn này như một kiểu dữ liệu bình thường. Điều đó không tốt sao? Điều gì sẽ xảy ra nếu không có bộ điều khiển điều hướng cho bộ điều khiển chế độ xem của bạn? Nếu bạn đề xuất rằng sẽ luôn có một giá trị được lưu trữ trong navigationController là sai? Ứng dụng của bạn sẽ gặp sự cố. Đơn giản và xấu xí như vậy.

Vì vậy, hãy sử dụng! chỉ khi bạn chắc chắn 101% rằng điều này là an toàn.

Còn nếu bạn không chắc chắn rằng sẽ luôn có một bộ điều khiển điều hướng? Sau đó, bạn có thể sử dụng? Thay vì một !:

controller.navigationController?.pushViewController(myViewController, animated: true)

Cái gì ? đằng sau tên thuộc tính cho trình biên dịch biết là tôi không biết thuộc tính này chứa nil hay một giá trị, vì vậy: nếu nó có giá trị, hãy sử dụng nó, và ngược lại chỉ cần xem xét toàn bộ biểu thức nil. Hiệu quả? cho phép bạn sử dụng thuộc tính đó chỉ trong trường hợp có bộ điều khiển điều hướng. Không nếu séc của bất kỳ loại nào hoặc đúc dưới bất kỳ hình thức nào. Cú pháp này là hoàn hảo khi bạn không quan tâm đến việc bạn có bộ điều khiển điều hướng hay không và chỉ muốn làm điều gì đó nếu có.

Rất cảm ơn Fantageek


8

Chúng là hai dạng Downcasting khác nhau trong Swift.

( as?) , được biết là Dạng có điều kiện , trả về một giá trị tùy chọn của kiểu mà bạn đang cố gắng truyền xuống.

Bạn có thể sử dụng nó khi không chắc liệu quá trình downcast có thành công hay không. Dạng toán tử này sẽ luôn trả về một giá trị tùy chọn và giá trị này sẽ bằng 0 nếu không thể thực hiện việc downcast. Điều này cho phép bạn kiểm tra xem có thành công không.


( as!) , được biết là Dạng bắt buộc , cố gắng hạ xuống và buộc mở ra kết quả như một hành động ghép đơn lẻ.

Bạn CHỈ nên sử dụng nó khi bạn chắc chắn rằng quá trình downcast sẽ luôn thành công. Dạng toán tử này sẽ gây ra lỗi thời gian chạy nếu bạn cố truyền xuống loại lớp không chính xác.

Để biết thêm chi tiết, vui lòng kiểm tra phần Type Casting trong tài liệu của Apple.


4

Có thể ví dụ mã này sẽ giúp ai đó tìm hiểu nguyên tắc:

var dict = [Int:Any]()
dict[1] = 15

let x = dict[1] as? String
print(x) // nil because dict[1] is an Int

dict[2] = "Yo"

let z = dict[2] as! String?
print(z) // optional("Yo")
let zz = dict[1] as! String // crashes because a forced downcast fails


let m = dict[3] as! String?
print(m) // nil. the forced downcast succeeds, but dict[3] has no value

Ngoài ra, hãy đặt z2 = dict [2] như! Chuỗi // "Yo" (không phải tùy chọn)
Jay

0

Đầu tiên là "ép kiểu có điều kiện" (xem "toán tử ép kiểu" trong tài liệu tôi đã liên kết) . Nếu ép kiểu thành công, giá trị của biểu thức được bao bọc trong một tùy chọn và được trả về, nếu không giá trị trả về là nil.

Điều thứ hai có nghĩa là chuỗi tùy chọn có thể là một đối tượng chuỗi hoặc nó có thể là nil.

Tìm thấy thêm thông tin trong câu hỏi liên quan này .



-1

Tôi là người mới làm quen với Swift và viết ví dụ này cố gắng giải thích khi tôi hiểu về 'tùy chọn'. Nếu tôi sai xin vui lòng sửa cho tôi.

Cảm ơn.


class Optional {

    var lName:AnyObject! = "1"

    var lastName:String!
}

let obj = Optional()

print(obj.lName)

print(obj.lName!)

obj.lastName = obj.lName as? String

print(obj.lastName)

(1): obj.lastName = obj.lName as! String

vs

(2): obj.lastName = obj.lName as? String

Trả lời: (1) Ở đây lập trình viên chắc chắn rằng “obj.lName”có chứa đối tượng kiểu chuỗi. Vì vậy, chỉ cần cung cấp giá trị đó cho “obj.lastName”.

Bây giờ, nếu lập trình viên đúng nghĩa "obj.lName"là đối tượng kiểu chuỗi, thì không vấn đề gì. "obj.lastName" sẽ được đặt thành cùng một giá trị.

Nhưng nếu lập trình viên sai có nghĩa "obj.lName"là không phải đối tượng kiểu chuỗi tức là nó chứa một số đối tượng kiểu khác như "NSNumber", v.v. Sau đó CRASH (Lỗi thời gian chạy).

(2) Lập trình viên không chắc chắn rằng “obj.lName”có chứa đối tượng kiểu chuỗi hoặc bất kỳ đối tượng kiểu nào khác. Vì vậy, hãy đặt giá trị đó thành “obj.lastName”nếu nó là kiểu chuỗi.

Bây giờ, nếu lập trình viên đúng nghĩa “obj.lName”là đối tượng kiểu chuỗi, thì không vấn đề gì. “obj.lastName”sẽ được đặt thành cùng một giá trị.

Nhưng nếu lập trình viên sai có nghĩa là obj.lName không phải là đối tượng kiểu chuỗi tức là nó chứa một số đối tượng kiểu khác như "NSNumber"vv Sau đó “obj.lastName”sẽ đặt thành giá trị nil. Vì vậy, No Crash (Happy :)

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.