วิธีป้องกันการกำหนดตัวแปรที่มีอยู่โดยไม่ต้องให้ / var ใน Swift 2

คีย์เวิร์ด guard นั้นยอดเยี่ยมมากและฉันก็ใช้มันมาตั้งแต่เปิดตัว Swift 2 ฉันสามารถใช้งานได้สำเร็จในหลาย ๆ สถานการณ์ รวมถึงสถานการณ์นี้ด้วย:

for viewController in self.navigationController!.viewControllers {
    guard let myCustomVC = viewController as? MyCustomViewController else {continue}

    myCustomVC.doWhatItHasToDo()
    break
}

ทำงานเหมือนมีเสน่ห์ วนซ้ำทั้งหมด viewControllers ในปัจจุบัน navigationController จนกว่าจะพบสิ่งที่ฉันต้องการ

ปัญหาคือ: ตอนนี้ฉันต้องอัปเดตโค้ดด้านบนเพื่อใช้ตัวแปร myCustomVC ซ้ำนอกขอบเขต for..in มันควรจะง่ายพอๆ กับการประกาศตัวแปรก่อนขอบเขต แต่ฉันไม่สามารถหาวิธีในการกำหนดตัวแปร guard ได้หากไม่มีคีย์เวิร์ด let หรือ var

รหัส สมมุติ ต่อไปนี้แสดงให้เห็นถึงสิ่งที่ฉันพยายามทำให้สำเร็จ แต่ Swift จะไม่ยอมให้ฉันทำเช่นนั้น:

var myCustomVC: MyCustomViewController
for viewController in self.navigationController!.viewControllers {
    guard myCustomVC = viewController as? MyCustomViewController else {continue}

    myCustomVC.doWhatItHasToDo()
    break
}

// Now myCustomVC is available outside the for...in scope
myCustomVC.doWhatever()

Xcode แนะนำให้ใช้ == แทน = เนื่องจากคาดว่าคำสั่ง guard จะเป็นบริบทบูลีนแทนที่จะเป็นการกำหนดตัวแปร

ข้อผิดพลาด Xcode

มีความคิดเห็นเกี่ยวกับวิธีการแก้ไขปัญหานี้หรือไม่?


person mathielo    schedule 18.12.2015    source แหล่งที่มา
comment
คุณสามารถใช้ตัวกรองเพื่อค้นหาคอนโทรลเลอร์ที่คุณต้องการได้หรือไม่?   -  person Michael Welch    schedule 19.12.2015
comment
ให้ myCustomVC = viewControllers.filter { /* ใส่ภาคแสดงที่นี่ */ }.first; myCustomVC.doWhatItHasToDo(); myCustomVC.doWhatever();   -  person Michael Welch    schedule 19.12.2015


คำตอบ (2)


ขออภัยที่ไม่ได้ตอบคำถามของคุณ ฉันไม่คิดว่าสิ่งที่คุณถามจะเป็นไปได้ แต่ฉันมีทางเลือกอื่น

ดูเหมือนว่าคุณกำลังเขียนเมธอด filter ด้วยตนเองซ้ำแล้วซ้ำอีก ฉันขอแนะนำให้ใช้ filter เพื่อค้นหาองค์ประกอบที่คุณต้องการ หากมีการแข่งขันมากกว่าหนึ่งรายการ ให้ใช้ first เพื่อแข่งขันรายการแรก ตัวอย่างเช่น ด้านล่าง ฉันเลือกเลขคู่ตัวแรกจากรายการ:

let myArray = [0,1,3,5,7,4,6,10,11]
let myNum = myArray.filter { $0 % 2 == 0 }.first

ฉันไม่ทราบไวยากรณ์ที่แน่นอนที่จะใช้กับเพรดิเคตของคุณ แต่มีลักษณะดังนี้

let myCustomVC = self.navigationController!.viewControllers.filter {
    $0 is MyCustomViewController }.first

// myCustomVC will be an optional because first returns an optional
if let myCustomVC = myCustomVC {
    myCustomVC.doWhatItHasToDo()
    myCustomVC.doWhatever()
}

หรือหากคุณมีการดำเนินการที่ต้องทำกับแต่ละองค์ประกอบที่ตรงกับภาคแสดง ให้ใช้ foreach

    let myCustomVC = self.navigationController!.viewControllers.filter {
    $0 is MyCustomViewController }.foreach { // action // }
person Michael Welch    schedule 18.12.2015
comment
ไมเคิล ตอบโจทย์สุดๆ! ขอบคุณสำหรับคำอธิบายโดยละเอียดและตัวอย่างที่ยอดเยี่ยม อันที่จริงฉันกำลัง สร้างวงล้อขึ้นมาใหม่ โดยที่ไม่รู้ตัว - person mathielo; 19.12.2015
comment
เย็น. ดีใจที่สิ่งนี้ตรงตามความต้องการของคุณ - person Michael Welch; 19.12.2015

ฉันยังคงคิดถึงคำถามของคุณ เป็นไปได้ที่จะทำสิ่งที่คุณถามในตอนแรก แต่วิธีแก้ปัญหาที่ฉันคิดขึ้นมานั้นซับซ้อนเล็กน้อย โดยพื้นฐานแล้วเกี่ยวข้องกับการกำหนดการปิดประเภท Any -> Bool ซึ่งคุณสามารถใช้ในคำสั่ง guard ได้ นี่คือตัวอย่างที่สมบูรณ์ บางทีถ้ามันมีประโยชน์ คุณก็อาจจะทำความสะอาดมันบ้างก็ได้ การวนซ้ำ for จริงนั้นไม่ได้แย่เลย สมมติว่าคุณได้กำหนดการปิดเพรดิเคตไว้ที่ใดที่หนึ่งแล้ว

class MyClass {}

var myCollection = [Any]()
// add sample elements 
myCollection.append("hello")
myCollection.append(MyClass())

// declare the variable to receive the "found" element
var myElement:MyClass? = nil

// define our predicate to use in guard statement
let myPredicate:Any->Bool = { obj in
    if let obj = obj as? MyClass {
        myElement = obj
        return true
    }
    return false
}

for v in myCollection {
    guard myPredicate(v)
    else {continue}

   // do something with v
}

if let myElement = myElement {
    print(myElement)
}

อัปเดต: ตัวอย่างที่ง่ายกว่ามาก (ยังต้องมีการอนุญาต)

for v in myCollection {
    guard let myObj = v as? MyClass else {continue}
    myElement = myObj
}
person Michael Welch    schedule 19.12.2015