Bagaimana memecahkan “Interpolasi string menghasilkan deskripsi debug untuk nilai opsional; apakah Anda bermaksud untuk membuat ini eksplisit? ” di Xcode 8.3 beta?

89

Sejak beta 8.3, zillions memperingatkan "Interpolasi string menghasilkan deskripsi debug untuk nilai opsional; apakah Anda bermaksud untuk membuatnya eksplisit?" muncul di kode saya.

Misalnya, peringatan muncul dalam situasi berikut, di mana opsi dapat mengarah ke nol:

let msg = "*** Error \(options["taskDescription"]): cannot load \(sUrl) \(error)"

Seperti yang dirancang sebelumnya, tidak masalah bagi saya (dan kompiler) opsional untuk diinterpolasi sebagai 'nil'. Tetapi compiler berubah pikiran.

Apa yang disarankan oleh compiler adalah menambahkan konstruktor String dengan deskripsi sebagai berikut:

let msg = "*** Error \(String(describing: options["taskDescription"])): cannot load \(sUrl) \(error)"

Jelas, hasilnya eksplisit tetapi juga menurut saya sangat tidak praktis. Apakah ada pilihan yang lebih baik? Apakah saya harus memperbaiki semua peringatan itu atau lebih baik menunggu beta berikutnya?

Tangkapan layar untuk deskripsi

Stéphane de Luca
sumber
28
Benar-benar peringatan yang menjengkelkan ...
Jonny
Swift 3merusak milik saya sendiri logdan saya membuat kesalahan dengan hanya menggunakan printsebagai gantinya. Harus selalu membuat pembungkus Anda sendiri jika tidak Anda akan kacau oleh "fitur baru" semacam ini.
superarts.org

Jawaban:

107

Ini adalah perubahan yang dibuat dalam permintaan tarik ini karena fakta bahwa interpolasi Optional(...)ke string resultan sering kali tidak diinginkan, dan bisa sangat mengejutkan dalam kasus dengan opsional yang tidak terbungkus secara implisit . Anda dapat melihat diskusi lengkap tentang perubahan ini di milis di sini .

Seperti yang disebutkan dalam diskusi pull request (meskipun sayangnya bukan oleh Xcode) - satu cara yang sedikit lebih baik untuk membungkam peringatan daripada yang digunakan String(describing:)adalah dengan menambahkan cast ke tipe opsional dari apa pun yang Anda interpolasi, jadi misalnya:

var i: Int? = 5
var d: Double? = nil

print("description of i: \(i as Int?)")    // description of i: Optional(5)
print("description of d: \(d as Double?)") // description of d: nil

Yang juga bisa digeneralisasikan untuk as Optional:

print("description of i: \(i as Optional)") // description of i: Optional(5)
print("description of d: \(d as Optional)") // description of d: nil

Di Swift 5, dengan sistem interpolasi string baru yang diperkenalkan oleh SE-0228 , opsi lainnya adalah menambahkan appendInterpolationkelebihan beban khusus untuk DefaultStringInterpolation:

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}

var i: Int? = 5
var d: Double? = nil

print("description of i: \(optional: i)") // description of i: Optional(5)
print("description of d: \(optional: d)") // description of d: nil

Dan, jika diinginkan, Anda bahkan dapat menghapus label argumen untuk menonaktifkan peringatan sepenuhnya di dalam modul (atau dalam file tertentu jika Anda menandainya sebagai fileprivate):

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(_ optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}

var i: Int? = 5
var d: Double? = nil

print("description of i: \(i)") // description of i: Optional(5)
print("description of d: \(d)") // description of d: nil

Meskipun secara pribadi saya lebih suka mempertahankan label argumen.

Hamish
sumber
Dari proposal tersebut, belum jelas apakah perubahan ini akan permanen? Bagaimana menurut anda? @Hamish
Stéphane de Luca
@ StéphanedeLuca Ada cukup banyak diskusi di milis tentang solusi lain seperti mengizinkan untuk ?? "nil"membungkam peringatan, yang tampaknya agak populer, sehingga mungkin muncul di proposal lain dalam waktu dekat. Saya setuju bahwa solusi ini kurang dari ideal - secara pribadi, saya merasa agak jelas mengharapkan Optional(...)untuk diinterpolasi ke dalam string untuk opsional yang kuat - hanya kasus IUO yang membutuhkan IMO peringatan ini. Tetapi Swift terus berkembang, jadi ini semua mungkin berubah di kemudian hari. Tapi untuk saat ini, itulah yang kami punya.
Hamish
Saya juga tersandung pada masalah yang agak 'terkait' dalam jika jangan membuka kotak lagi di sini stackoverflow.com/questions/42543512/… apakah Anda dapat melihatnya? @Hamish
Stéphane de Luca
... dalam hal apapun kode ini gila:guard result == nil else { print("result was \(result as Optional)") return }
loretoparisi
1
@loretoparisi Mengapa tidak digunakan if let? mis if let result = result { print("result was \(result)"); return }. Tidak semua pengembalian awal harus dilakukan dengan penjaga.
Hamish
28

Dua cara yang lebih mudah untuk menangani masalah ini.

Pilihan 1:

Yang pertama adalah dengan "membuka paksa" nilai yang ingin Anda kembalikan menggunakan bang (!)

var someValue: Int? = 5
print(someValue!)

Keluaran:

5

Pilihan 2:

Cara lain, yang bisa menjadi cara yang lebih baik - adalah "membuka dengan aman" nilai yang ingin Anda kembalikan.

var someValue: Int? = 5

if let newValue = someValue {
    print(newValue)
}

Keluaran:

5

Sarankan untuk pergi dengan opsi 2.

Tip: Hindari force unwrapping (!) Jika memungkinkan karena kami tidak yakin apakah kami akan selalu memiliki nilai yang akan dibuka.

Mo Iisa
sumber
1
Saya baru tetapi saya suka opsi 2 untuk memvalidasi pembungkusan sebelum dicetak dan Anda selalu memiliki opsi untuk mencetak sesuatu yang lain saat bungkusnya dibuka
AbuTaareq
membuka paksa bungkusan adalah NO-GO jika Anda ingin menganggap ini serius. Maksud saya, hanya karena Anda ingin konsol mencetak sesuatu, Anda berisiko merusak aplikasi? Itu saran yang sangat buruk. Opsi 2 tidak apa-apa, saya pikir 2ht "Tip" tidak cukup, ubah jawabannya!
Martin Mlostek
16

tampaknya menggunakan String (menjelaskan: opsional) paling sederhana.

nilai default ?? tidak masuk akal untuk non-Strings misalnya Int.
Jika Int nil maka Anda ingin log yang menampilkan 'nil' bukan default ke Int lain misalnya 0.

Beberapa kode taman bermain untuk diuji:

var optionalString : String? = nil
var optionalInt : Int? = nil

var description_ = ""
description_ = description_ + "optionalString: \(String(describing: optionalString))\r"
description_ = description_ + "   optionalInt: \(String(describing: optionalInt))\r"

print(description_)

Keluaran

optionalString: nil
optionalInt: nil
brian.clear
sumber
13

Setelah memperbarui ke Xcode 8.3 dan mendapatkan banyak pesan peringatan, saya menemukan yang berikut ini yang lebih seperti perilaku keluaran asli, mudah ditambahkan, mengurangi penggunaan "String (menjelaskan :)" baik dalam kode maupun keluaran .

Pada dasarnya, tambahkan ekstensi Opsional yang memberikan String yang menjelaskan hal tersebut dalam opsional, atau cukup "nil" jika tidak disetel. Selain itu, jika yang ada di opsional adalah String, masukkan tanda kutip.

extension Optional {
    var orNil : String {
        if self == nil {
            return "nil"
        }
        if "\(Wrapped.self)" == "String" {
            return "\"\(self!)\""
        }
        return "\(self!)"
    }
}

Dan penggunaan di taman bermain:

var s : String?
var i : Int?
var d : Double?

var mixed = "s = \(s.orNil)    i = \(i.orNil)   d = \(d.orNil)" // "s = nil    i = nil   d = nil"

d = 3
i = 5
s = ""
mixed = "s = \(s.orNil)    i = \(i.orNil)   d = \(d.orNil)" // "s = ""    i = 5   d = 3.0"

s = "Test"
d = nil
mixed = "s = \(s.orNil)    i = \(i.orNil)   d = \(d.orNil)" // "s = "Test"    i = 5   d = nil"

Terima kasih atas bantuan dari tautan berikut:

check-if-variable-is-an-optional-and-what-type-it-wraps

anorskdev
sumber
Solusi ini tidak berfungsi dalam rantai opsional. Suka a?.b?.c.orNil.
Vincent Duduk
11

Lihat perbaikan Ole Begeman untuk ini . Aku menyukainya. Ini menciptakan ???operator yang kemudian dapat Anda gunakan seperti ini:

var someValue: Int? = 5
print("The value is \(someValue ??? "unknown")")
// → "The value is 5"
someValue = nil
print("The value is \(someValue ??? "unknown")")
// → "The value is unknown"
dar512
sumber
5
Referensi ke posting blognya yang menjelaskan ini akan berguna Saya pikir: oleb.net/blog/2016/12/optionals-string-interpolation
Nicolai Henriksen
8

Klik dua kali pada segitiga kuning yang ditampilkan pada baris yang berisi peringatan ini. Ini akan menampilkan FixIt dengan dua solusi.

Tangkapan layar ditambahkan

  1. Menggunakan String(describing:) untuk membungkam peringatan ini:

    Menggunakan ini akan menjadi String(describing:<Variable>)

    Misalnya. :String(describing: employeeName)

  2. Sediakan sebuah default value untuk menghindari peringatan ini:

    Menggunakan ini akan menjadi (<Variable> ?? default value)

    Misalnya.: employeeName ?? “Anonymous” as! String

Jayprakash Dubey
sumber
1
Ya, saya juga akan memilih Operator Penggabungan Nihil: developer.apple.com/library/content/documentation/Swift/…
kevinius
1
Jawaban yang bagus! Penggabungan nol berfungsi baik dengan ini jika Anda memiliki nilai string alternatif untuk diberikan
Lance Samaria
2

Cepat 5

Solusi saya adalah membuat extensionyang unwrap Optionalobjek untukAny .

Saat Anda log objek atau mencetaknya, Anda dapat melihat aktual objectatau <nil>⭕️(kombinasi dari teks dan karakter visual). Ini berguna untuk dilihat, terutama di log konsol.

extension Optional {
    var logable: Any {
        switch self {
        case .none:
            return "<nil>|⭕️"
        case let .some(value):
            return value
        }
    }
}

// sample
var x: Int?
print("Logging optional without warning: \(x.logable)")
// → Logging optional without warning: <nil>|⭕️
nahung89
sumber
0

Buat metode interpolasi yang menerima Jenis generik opsional dengan parameter tanpa nama. Semua peringatan Anda yang mengganggu akan hilang secara ajaib.

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(_ optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}
ScottyBlades
sumber