Câu hỏi Downcasting tùy chọn trong Swift: như? Nhập, hoặc là! Kiểu?


Đưa ra những điều sau trong Swift:

var optionalString: String?
let dict = NSDictionary()

Sự khác nhau thực tế giữa hai câu sau đây là gì:

optionalString = dict.objectForKey("SomeKey") as? String

so với

optionalString = dict.objectForKey("SomeKey") as! String?

76
2017-09-07 09:07


gốc


Xem loại truyền trong Swift 2 fantageek.com/blog/2015/09/23/type-casting-in-swift - onmyway133
Aslo See The as! Nhà điều hành từ Apple - Honey


Các câu trả lời:


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

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

optionalString sẽ là một loại biến String?. Nếu loại cơ bản là một cái gì đó khác hơn là String điều này sẽ vô hại chỉ cần gán nil đến 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 optionalString thuộc loại String?, nhưng nó sẽ sụp đổ nếu kiểu cơ bản là cái gì đó khác.

Kiểu đầu tiên sau đó được sử dụng với if let để gỡ bỏ 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)
}

121
2017-09-07 12:28



Không phải là phương pháp đầu tiên luôn tốt hơn? Cả hai trả về một tùy chọn kiểu String? Dường như phương pháp thứ hai cũng giống như phương pháp thứ nhất nhưng có thể bị hỏng nếu downcast không thành công. Vậy tại sao lại sử dụng nó? - Sikander
Có @Sikander, người đầu tiên luôn tốt hơn. Tôi sẽ không bao giờ sử dụng thứ hai. - vacawama


Để 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

10
2018-03-27 18:10



1 cho ví dụ của bạn nhưng bạn có thể giải thích cho tôi với cùng một ví dụ để sử dụng như! thay vì như thế nào? trong khi downcasting như để cho cell = tableView.dequeueReusableCellWithIdentifier ("Cell") thành! UITableViewCell..i đoán là? là đủ tại sao nhu cầu như vậy! - Anish 웃
let cell = tableView.dequeueReusableCellWithIdentifier ("Cell") 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ận dạng "Ô" thành UITableViewCell có hay không. Nếu nill sau đó nó trả về nill (vì vậy chúng tôi tránh tai nạn ở đây). - jishnu bala
hấp dẫn, intNil as! String? // ==nil không gây ra sự cố !!! ???, tùy chọn <Int> .Không khác với Tùy chọn <Chuỗi>. - onmyway133
tại sao bạn downcast as? đến String? Tại sao bạn không downcast nó để String? ? Tại sao bạn không downcast as! đến String? - Honey
Cố gắng làm sân chơi này trong Swift 3, nhưng bạn phải sử dụng Any thay vì AnyObject - Honey


as? Types - có nghĩa là quá trình đúc xuống là tùy chọn. Quá trình này có thể thành công hay không (hệ thống sẽ trả về nil nếu down casting thất bại) .Bất cứ cách nào sẽ không sụp đổ nếu down casting thất bại.

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

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

Chúng ta hãy 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 xuống đúc của tế bào với định danh "Cell" để UITableViewCell là 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 ta tránh sự cố ở đây). Ở đây chúng ta 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 nhớ nó như thế này - Nếu ? nó có nghĩa là chúng ta không chắc liệu giá trị là không hay không (dấu hỏi đến khi chúng ta không biết mọi thứ).

  2. Tương phản với:

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

    Ở đây chúng tôi nói với trình biên dịch mà xuống đúc nên thành công. Nếu nó không hệ thống sẽ sụp đổ. Vì vậy, chúng tôi cung cấp ! khi chúng ta chắc chắn giá trị đó không phải là không.


10
2018-06-04 17:21





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

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

Bạn có thể sử dụng nó khi bạn không chắc chắn nếu downcast sẽ thành công.   Biểu mẫu của toán tử này sẽ luôn trả về một giá trị tùy chọn, và   giá trị sẽ là nil nếu downcast là không thể. Điều này cho phép   bạn để kiểm tra cho một downcast thành công.


(as!), được biết là Biểu mẫu bắt buộc, cố gắng downcast và buộc unwraps kết quả như là một hành động hợp chất duy nhất.

Bạn nên sử dụng nó CHỈ CÓ khi bạn chắc chắn rằng   luôn thành công. Biểu mẫu này của toán tử sẽ kích hoạt thời gian chạy   lỗi nếu bạn cố gắng downcast đến một loại lớp không chính xác.

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


7
2017-07-08 10:20





  • as được sử dụng cho upcasting và gõ đúc để loại cầu nối
  • as? được sử dụng để đúc an toàn, trả lại nil nếu không thành công
  • as! được sử dụng để ép đúc, lỗi nếu không thành công

Chú thích:

  • as! không thể truyền kiểu 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 ? ngay sau kiểu dữ liệu bạn nói với trình biên dịch rằng biến có thể chứa một số hay không. Khéo léo! Lưu ý rằng việc xác định các hằng số tùy chọn không thực sự có ý nghĩa - 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ó phải là không hay không.

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

giả sử chúng ta 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 xem của chúng tôi và muốn trình bày một bộ điều khiển xem mới trên đầu trang của 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 có một bộ điều khiển chuyển hướng đặ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ó để điều khiển bộ điều khiển chế độ xem hoặc bật. 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, do đó trình đ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à trường hợp đối với 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 tôi. Nếu bạn biết một thực tế là bộ điều khiển xem của bạn sẽ luôn luôn có một bộ điều khiển điều hướng, bạn có thể tiếp tục và buộc unwrap nó:

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

Khi bạn đặt một! đằng sau tên thuộc tính 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 cửa hàng giá trị để xử lý tùy chọn này như một kiểu dữ liệu bình thường. À không đẹp sao? Điều gì sẽ xảy ra mặc dù 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 gợi ý 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ẽ bị lỗi. Đơn giản và xấu xí như vậy.

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

Còn nếu bạn không chắc chắn sẽ luôn có 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)

Cai gi ? đằng sau tên thuộc tính cho trình biên dịch biết Tôi không biết liệu thuộc tính này có chứa giá trị nil hay không, vì vậy: nếu nó có giá trị sử dụng nó, và oterwise chỉ xem xét toàn bộ biểu thức nil. Có 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 hoặc đúc bất kỳ loại nào. Cú pháp này là hoàn hảo khi bạn không quan tâm liệu 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


4
2018-03-31 20:20





Có lẽ ví dụ mã này sẽ giúp ai đó mò mẫm 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

2
2017-07-03 18:54



Ngoài ra, hãy để z2 = dict [2] là! Chuỗi // "Yo" (không bắt buộc) - Jay


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

Điều thứ hai có nghĩa là optionalString có thể là một đối tượng chuỗi hoặc nó có thể là số không.

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


0
2017-09-07 09:14