วิธีแก้ปัญหา“ การแก้ไขสตริงสร้างคำอธิบายการดีบักสำหรับค่าที่เป็นทางเลือก คุณตั้งใจที่จะทำให้มันชัดเจนหรือไม่” ใน Xcode 8.3 beta?


89

ตั้งแต่เบต้า 8.3 เป็นต้นไปคำเตือน zillions "การแก้ไขสตริงจะสร้างคำอธิบายการดีบักสำหรับค่าที่ไม่บังคับคุณตั้งใจจะทำให้สิ่งนี้ชัดเจนหรือไม่" ปรากฏในรหัสของฉัน

ตัวอย่างเช่นคำเตือนปรากฏขึ้นในสถานการณ์ต่อไปนี้ซึ่งตัวเลือกอาจนำไปสู่ศูนย์:

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

ตามที่ออกแบบไว้ก่อนหน้านี้ฉัน (และคอมไพเลอร์) ก็โอเคสำหรับฉันที่จะแก้ไขตัวเลือกเป็น 'ศูนย์' แต่คอมไพเลอร์เปลี่ยนใจ

สิ่งที่คอมไพเลอร์แนะนำคือการเพิ่มตัวสร้างสตริงพร้อมคำอธิบายดังนี้:

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

เห็นได้ชัดว่าผลลัพธ์นั้นชัดเจน แต่ก็ยุ่งยากมากในความคิดของฉัน มีตัวเลือกที่ดีกว่านี้หรือไม่? ฉันต้องแก้ไขคำเตือนเหล่านั้นทั้งหมดหรือดีกว่ารอเบต้าถัดไป

ภาพหน้าจอสำหรับคำอธิบาย


28
ช่างเป็นคำเตือนที่น่ารำคาญจริงๆ ...
จอนนี่

Swift 3ทำลายตัวฉันเองlogและฉันทำผิดพลาดเพียงแค่ใช้printแทน ควรสร้างกระดาษห่อหุ้มของคุณเองเสมอมิฉะนั้นคุณจะถูก "คุณลักษณะใหม่" ประเภทนี้
superarts.org

คำตอบ:


107

นี่คือการเปลี่ยนแปลงที่ถูกสร้างขึ้นมาในคำขอดึงนี้เนื่องจากความจริงที่ว่า interpolating Optional(...)เข้าไปในสตริงผลลัพธ์มักจะเป็นที่ไม่พึงประสงค์และโดยเฉพาะอย่างยิ่งจะแปลกในกรณีที่มี optionals ท่านสามารถเข้าดูการสนทนาแบบเต็มของการเปลี่ยนแปลงนี้ในรายชื่อทางไปรษณีย์ที่นี่

ดังที่ได้กล่าวไว้ในการอภิปรายคำขอดึงข้อมูล (แม้ว่าจะไม่ใช่ Xcode ก็ตาม) - วิธีหนึ่งที่ดีกว่าเล็กน้อยในการปิดเสียงเตือนมากกว่าการใช้String(describing:)คือการเพิ่มนักแสดงลงในประเภทที่เป็นทางเลือกของสิ่งที่คุณกำลังแก้ไขตัวอย่างเช่น:

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

ซึ่งสามารถสรุปได้ว่า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

ใน Swift 5 ที่มีระบบแก้ไขสตริงใหม่ที่SE-0228 แนะนำอีกทางเลือกหนึ่งคือการเพิ่มappendInterpolationโอเวอร์โหลดแบบกำหนดเองสำหรับ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

และหากต้องการคุณสามารถลบป้ายอาร์กิวเมนต์เพื่อปิดใช้งานคำเตือนทั้งหมดภายในโมดูล (หรือภายในไฟล์ใดไฟล์หนึ่งหากคุณทำเครื่องหมายเป็น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

แม้ว่าโดยส่วนตัวแล้วฉันอยากจะเก็บป้ายกำกับอาร์กิวเมนต์ไว้


จากข้อเสนอยังไม่ชัดเจนว่าการเปลี่ยนแปลงนี้จะถาวรหรือไม่? คุณคิดอย่างไร? @ Hamish
Stéphane de Luca

@ StéphanedeLucaมีการพูดคุยกันเล็กน้อยในรายชื่ออีเมลเกี่ยวกับวิธีแก้ปัญหาอื่น ๆ เช่นการอนุญาตให้?? "nil"ปิดเสียงคำเตือนซึ่งดูเหมือนจะได้รับความนิยมไม่มากนักดังนั้นอาจปรากฏในข้อเสนออื่นในอนาคตอันใกล้นี้ ฉันยอมรับว่าวิธีแก้ปัญหานี้น้อยกว่าอุดมคติโดยส่วนตัวฉันรู้สึกว่ามันค่อนข้างชัดเจนที่คาดว่าOptional(...)จะถูกสอดแทรกเข้าไปในสตริงสำหรับตัวเลือกที่แข็งแกร่งซึ่งเป็นกรณีของ IUO เท่านั้นที่ต้องการ IMO คำเตือนนี้ แต่ Swift มีการพัฒนาอย่างต่อเนื่องดังนั้นสิ่งนี้อาจเปลี่ยนแปลงได้ในภายหลัง แต่สำหรับตอนนี้มันคือสิ่งที่เรามี
Hamish

ฉันยังสะดุดกับปัญหาที่ 'เกี่ยวข้อง' ใน if let not unboxing อีกต่อไปที่นี่stackoverflow.com/questions/42543512/…ถ้าคุณสามารถดูได้ไหม @ Hamish
Stéphane de Luca

... ไม่ว่าในกรณีใดรหัสนี้คือความguard result == nil else { print("result was \(result as Optional)") return }
บ้าคลั่ง

1
@loretoparisi ทำไมไม่ใช้if let? กล่าวคือif let result = result { print("result was \(result)"); return }. การกลับก่อนกำหนดไม่จำเป็นต้องทำกับยาม
Hamish

28

สองวิธีที่ง่ายกว่าในการจัดการกับปัญหานี้

ตัวเลือกที่ 1:

อย่างแรกคือการ"บังคับแกะ" ค่าที่คุณต้องการส่งคืนโดยใช้ปัง(!)

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

เอาท์พุต:

5

ทางเลือกที่ 2:

อีกวิธีหนึ่งซึ่งอาจเป็นวิธีที่ดีกว่านั้นคือการ"แกะออกอย่างปลอดภัย"ค่าที่คุณต้องการส่งคืน

var someValue: Int? = 5

if let newValue = someValue {
    print(newValue)
}

เอาท์พุต:

5

ขอแนะนำให้ไปกับตัวเลือกที่ 2

เคล็ดลับ: หลีกเลี่ยงการบังคับแกะ (!) หากเป็นไปได้เนื่องจากเราไม่แน่ใจว่าจะมีค่าที่จะแกะออกมาหรือไม่


1
ฉันเป็นคนใหม่ แต่ฉันชอบตัวเลือกที่ 2 เพื่อตรวจสอบการห่อก่อนพิมพ์และคุณจะมีตัวเลือกในการพิมพ์อย่างอื่นเสมอเมื่อไม่ได้ห่อ
AbuTaareq

การบังคับให้แกะเป็นสิ่งที่ไม่ควรทำหากคุณต้องการดำเนินการอย่างจริงจัง ฉันหมายถึงเพียงเพราะคุณต้องการคอนโซลพิมพ์บางสิ่งที่คุณมีความเสี่ยงที่จะทำให้แอปล่ม? นั่นเป็นข้อเสนอแนะที่แย่มาก ตัวเลือกที่ 2 ไม่เป็นไรฉันคิดว่า "เคล็ดลับ" 2ht ไม่เพียงพอเปลี่ยน anwer!
Martin Mlostek

16

ดูเหมือนว่าการใช้ String (อธิบาย: ทางเลือก) นั้นง่ายที่สุด

ค่าเริ่มต้น ?? ไม่มีเหตุผลสำหรับ non-Strings เช่น Int
หาก Int เป็นศูนย์คุณต้องการให้บันทึกแสดง 'ศูนย์' ไม่ใช่ค่าเริ่มต้นเป็น Int อื่นเช่น 0

รหัสสนามเด็กเล่นที่จะทดสอบ:

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

เอาต์พุต

optionalString: nil
optionalInt: nil

13

หลังจากอัปเดตเป็น Xcode 8.3 และได้รับข้อความเตือนจำนวนมากฉันได้พบกับสิ่งต่อไปนี้ที่เหมือนกับพฤติกรรมเอาต์พุตดั้งเดิมเพิ่มเข้ามาได้ง่ายลดความฟุ่มเฟือยของการใช้ "String (อธิบาย :)" ทั้งในโค้ดและเอาต์พุต .

โดยพื้นฐานแล้วให้เพิ่มส่วนขยายที่เป็นทางเลือกที่ให้สตริงอธิบายสิ่งต่างๆในตัวเลือกหรือ "ศูนย์" หากไม่ได้ตั้งค่า นอกจากนี้หากสิ่งที่อยู่ในตัวเลือกเป็นสตริงให้ใส่ในเครื่องหมายคำพูด

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

และการใช้งานในสนามเด็กเล่น:

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"

ขอบคุณสำหรับความช่วยเหลือจากลิงค์ต่อไปนี้:

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


โซลูชันนี้ไม่ทำงานในห่วงโซ่เสริม ชอบa?.b?.c.orNil.
Vincent Sit

11

ดูแก้ไข Ole Begeman สำหรับนี้ ฉันรักมัน. จะสร้าง???โอเปอเรเตอร์ซึ่งคุณสามารถใช้ดังนี้:

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"

5
การอ้างอิงถึงบล็อกโพสต์ของเขาที่อธิบายสิ่งนี้จะเป็นประโยชน์ฉันคิดว่า: oleb.net/blog/2016/12/optionals-string-interpolation
Nicolai Henriksen

8

ดับเบิลคลิกที่สามเหลี่ยมสีเหลืองที่แสดงบนบรรทัดที่มีคำเตือนนี้ สิ่งนี้จะแสดงFixItพร้อมสองโซลูชัน

เพิ่มภาพหน้าจอแล้ว

  1. ใช้String(describing:)เพื่อปิดเสียงคำเตือนนี้:

    ใช้สิ่งนี้มันจะกลายเป็น String(describing:<Variable>)

    เช่น. :String(describing: employeeName)

  2. ระบุdefault valueเพื่อหลีกเลี่ยงคำเตือนนี้:

    ใช้สิ่งนี้มันจะกลายเป็น (<Variable> ?? default value)

    เช่น.: employeeName ?? “Anonymous” as! String


1
ใช่ฉันจะไปหา Nil-Coalescing Operator ด้วย: developer.apple.com/library/content/documentation/Swift/…
kevinius

1
ตอบโจทย์มาก! Nil-coalescing ใช้ได้ดีกับสิ่งนี้หากคุณมีค่าสตริงทางเลือกที่จะระบุ
Lance Samaria

2

สวิฟต์ 5

วิธีแก้ปัญหาของฉันคือการสร้างวัตถุที่extensionแกะOptionalออกAnyมา

เมื่อคุณบันทึกวัตถุหรือพิมพ์ออกมาคุณจะเห็นสิ่งที่เป็นจริงobjectหรือ<nil>⭕️(รวมกันจากข้อความและอักขระภาพ) มีประโยชน์ในการดูโดยเฉพาะในบันทึกของคอนโซล

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>|⭕️

0

สร้างวิธีการแก้ไขที่ยอมรับชนิดทั่วไปที่เป็นทางเลือกโดยมีพารามิเตอร์ที่ไม่มีชื่อ คำเตือนที่น่ารำคาญทั้งหมดของคุณจะหายไปอย่างน่าอัศจรรย์

extension DefaultStringInterpolation {
  mutating func appendInterpolation<T>(_ optional: T?) {
    appendInterpolation(String(describing: optional))
  }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.