Pertanyaan Pilihan Downcast di Swift: as? Ketik, atau sebagai! Mengetik?


Diberikan yang berikut di Swift:

var optionalString: String?
let dict = NSDictionary()

Apa perbedaan praktis antara dua pernyataan berikut:

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

vs

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

76
2017-09-07 09:07


asal


Jawaban:


Perbedaan praktisnya adalah ini:

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

optionalString akan menjadi variabel bertipe String?. Jika jenis yang mendasari adalah sesuatu selain dari String ini tidak berbahaya hanya dengan menugaskan nil ke opsional.

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

Ini mengatakan, saya tahu benda ini adalah a String?. Ini juga akan menghasilkan optionalString menjadi tipe String?, tapi itu akan crash jika tipe yang mendasari adalah sesuatu yang lain.

Gaya pertama kemudian digunakan dengan if let untuk membukanya dengan aman opsional:

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



Untuk mengklarifikasi apa yang dikatakan vacawama, berikut ini contohnya ...

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



as? Types - berarti proses pengecoran bawah bersifat opsional. Proses ini bisa berhasil atau tidak (sistem akan kembali nihil jika gagal turun). Cara apa pun tidak akan gagal jika pengecilan gagal.

as! Type? - Di sini proses pengecoran bawah harus berhasil (! mengindikasikan bahwa) . Tanda tanya akhir menunjukkan apakah hasil akhir bisa nihil atau tidak.

Info lebih lanjut tentang "!" dan "?"

Mari kita ambil 2 kasus

  1. Mempertimbangkan:

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

    Di sini kita tidak tahu apakah hasil pengecoran sel dengan pengenal "Sel" untuk UITableViewCell berhasil atau tidak. Jika tidak berhasil maka ia mengembalikan nol (jadi kami menghindari kecelakaan di sini). Di sini kita bisa melakukan seperti yang diberikan di bawah ini.

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

    Jadi mari kita mengingatnya seperti ini - Jika ? itu berarti kita tidak yakin apakah nilai tidak ada atau tidak (tanda tanya muncul ketika kita tidak tahu hal-hal).

  2. Bandingkan hal itu dengan:

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

    Di sini kami memberi tahu compiler bahwa down casting harus sukses. Jika gagal, sistem akan crash. Jadi kami memberi ! ketika kami yakin bahwa nilainya tidak nol.


10
2018-06-04 17:21



Mereka adalah dua bentuk yang berbeda Downcasting di Swift.

(as?), yang dikenal sebagai Formulir Bersyarat, mengembalikan nilai opsional dari jenis yang Anda coba untuk ditolak.

Anda dapat menggunakannya ketika Anda tidak yakin apakah orang yang gagal akan berhasil.   Bentuk operator ini akan selalu mengembalikan nilai opsional, dan   nilainya akan nihil jika yang putus asa tidak mungkin. Ini memungkinkan   Anda untuk memeriksa downcast yang sukses.


(as!), yang dikenal sebagai Formulir Paksaan, mencoba menurunkan dan memaksa-membuka kemungkinan hasil sebagai tindakan majemuk tunggal.

Anda harus menggunakannya HANYA ketika Anda yakin orang yang putus asa akan melakukannya   selalu berhasil. Bentuk operator ini akan memicu runtime   kesalahan jika Anda mencoba untuk downcast ke jenis kelas yang salah.

Untuk lebih jelasnya, silakan periksa Ketik Pengecoran bagian dari dokumentasi Apple.


7
2017-07-08 10:20



  • as digunakan untuk upcasting dan jenis casting ke tipe bridge
  • as? digunakan untuk pengecoran aman, kembali nihil jika gagal
  • as! digunakan untuk memaksa casting, crash jika gagal

catatan:

  • as! tidak dapat mentransmisikan jenis mentah ke opsional

Contoh:

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?)

Contoh

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

Dengan menambahkan ? segera setelah tipe data Anda memberi tahu compiler bahwa variabel tersebut mungkin berisi nomor atau tidak. Rapi! Perhatikan bahwa tidak benar-benar masuk akal untuk menentukan konstanta Opsional - Anda dapat menetapkan nilainya hanya sekali dan oleh karena itu Anda akan dapat mengatakan apakah nilainya tidak ada atau tidak.

Kapan kita harus menggunakan "?" dan kapan "!"

katakanlah kita memiliki aplikasi sederhana berbasis UIKit. kami memiliki beberapa kode di controller pandangan kami dan ingin menyajikan controller tampilan baru di atasnya. dan kita perlu memutuskan untuk mendorong tampilan baru di layar menggunakan pengontrol navigasi.

Seperti yang kita tahu setiap turunan ViewController memiliki pengendali navigasi properti. Jika Anda membuat aplikasi berbasis pengontrol navigasi, properti pengontrol tampilan master aplikasi ini disetel secara otomatis dan Anda dapat menggunakannya untuk mendorong atau menampilkan pengontrol tampilan. Jika Anda menggunakan satu template proyek aplikasi - tidak akan ada pengontrol navigasi yang dibuat secara otomatis untuk Anda, sehingga pengontrol tampilan default aplikasi Anda tidak akan memiliki apa pun yang disimpan di properti navigationController.

Saya yakin Anda sudah dapat menebak bahwa ini merupakan kasus untuk jenis data Opsional. Jika Anda memeriksa UIViewController Anda akan melihat bahwa properti didefinisikan sebagai:

var navigationController: UINavigationController? { get }

Jadi, kembali ke kasus penggunaan kami. Jika Anda tahu fakta bahwa pengontrol tampilan Anda akan selalu memiliki pengontrol navigasi, Anda dapat melanjutkan dan memaksa membukanya:

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

Ketika Anda meletakkan! di belakang nama properti Anda memberi tahu compiler Saya tidak peduli bahwa properti ini opsional, saya tahu bahwa ketika kode ini dijalankan di sana akan selalu ada penyimpanan nilai jadi perlakukan ini Opsional seperti jenis data normal. Bukankah itu bagus? Apa yang akan terjadi meskipun jika tidak ada pengontrol navigasi ke pengontrol tampilan Anda? Jika Anda menyarankan bahwa akan selalu ada nilai yang disimpan di navigationController salah? Aplikasi Anda akan mogok. Sederhana dan jelek seperti itu.

Jadi, gunakan! hanya jika Anda 101% yakin bahwa ini aman.

Bagaimana jika Anda tidak yakin akan selalu ada pengontrol navigasi? Maka Anda bisa menggunakannya? bukannya!:

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

Apa itu? di belakang nama properti memberitahu compiler Saya tidak tahu apakah properti ini mengandung nil atau nilai, jadi: jika memiliki nilai yang menggunakannya, dan secara otomatis hanya mempertimbangkan seluruh persamaan nihil. Secara efektif? memungkinkan Anda untuk menggunakan properti itu hanya dalam kasus ada pengendali navigasi. Tidak, jika ada pemeriksaan apa pun atau casting dalam bentuk apa pun. Sintaks ini sempurna ketika Anda tidak peduli apakah Anda memiliki pengontrol navigasi atau tidak, dan ingin melakukan sesuatu hanya jika ada.

Terima kasih banyak Fantageek


4
2018-03-31 20:20



Mungkin contoh kode ini akan membantu seseorang memahami prinsip:

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



Yang pertama adalah "pemain bersyarat" (lihat di bawah "operator pengecoran jenis" di dokumentasi yang telah saya tautkan). Jika pemain berhasil, nilai ekspresi dibungkus dalam opsional dan dikembalikan, jika tidak, nilai yang dikembalikan adalah nol.

Yang kedua berarti optionalString bisa menjadi objek string atau mungkin nihil.

Informasi lebih lanjut ditemukan dalam pertanyaan terkait ini.


0
2017-09-07 09:14