พารามิเตอร์การปิดการหลบหลีกที่รวดเร็วและเป็นตัวเลือก


162

ได้รับ:

typealias Action = () -> ()

var action: Action = { }

func doStuff(stuff: String, completion: @escaping Action) {
    print(stuff)
    action = completion
    completion()
}

func doStuffAgain() {
    print("again")
    action()
}

doStuff(stuff: "do stuff") { 
    print("swift 3!")
}

doStuffAgain()

มีวิธีใดที่จะทำให้completionพารามิเตอร์ (และaction) ของประเภทAction?และเก็บไว้ด้วย@escaping?

การเปลี่ยนชนิดจะทำให้เกิดข้อผิดพลาดต่อไปนี้:

คุณลักษณะ @escaping ใช้กับประเภทฟังก์ชันเท่านั้น

การลบ@escapingแอ็ตทริบิวต์โค้ดจะคอมไพล์และรัน แต่ดูเหมือนจะไม่ถูกต้องเนื่องจากการcompletionปิดกำลังหลบหนีขอบเขตของฟังก์ชัน


21
"การถอด@escapingแอตทริบิวต์ที่คอมไพล์รหัสและวิ่ง" - นั่นเพราะที่อธิบายไว้ในSR-2444 , Action?เป็นตามค่าเริ่มต้นการหลบหนี ดังนั้นการลบ@escapingเมื่อใช้การปิดที่เป็นตัวเลือกทำให้ได้สิ่งที่คุณต้องการ
Rob

ที่เกี่ยวข้อง: การอัปเดตการปิดไปสวิฟท์ 3 - @escaping
dfri

การปิดชื่อแทนประเภทกำลังหลบหนี
Masih

นี่คือบทความที่ยอดเยี่ยมโดย Ole Begemannที่อธิบายว่าทำไมมันถึงเกิดขึ้นและมีวิธีแก้ปัญหาบางอย่างหากคุณต้องการพารามิเตอร์ทางเลือกให้เป็น @noescape
เหตุผล

คำตอบ:


122

มีการรายงานSR-2552ที่@escapingไม่รู้จักนามแฝงประเภทฟังก์ชัน @escaping attribute only applies to function typesที่ว่าทำไมข้อผิดพลาด คุณสามารถแก้ไขปัญหาโดยการขยายประเภทฟังก์ชั่นในฟังก์ชั่นลายเซ็น:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: (@escaping ()->())?) {
    print(stuff)
    action = completion
    completion?()
}

func doStuffAgain() {
    print("again")
    action?()
}

doStuff(stuff: "do stuff") {
    print("swift 3!")
}

doStuffAgain()

แก้ไข 1 ::

ฉันจริงภายใต้ xcode 8 รุ่นเบต้าที่ข้อผิดพลาดSR-2552ยังไม่ได้รับการแก้ไข แก้ไขข้อผิดพลาดนั้นแนะนำอันใหม่ (อันที่คุณเผชิญ) ที่ยังเปิดอยู่ ดูSR-2444

วิธีแก้ปัญหา@Michael Ilsemanชี้เป็นวิธีแก้ปัญหาชั่วคราวลบ@escapingแอตทริบิวต์จากประเภทฟังก์ชั่นตัวเลือกที่ให้ฟังก์ชั่นในขณะที่หลบหนี

func doStuff(stuff: String, completion: Action?) {...}

แก้ไข 2 ::

SR-2444ได้ถูกปิดการระบุอย่างชัดเจนว่าการปิดในพารามิเตอร์ตำแหน่งไม่ได้หลบหนีและต้องการให้มีการทำเครื่องหมาย@escapingเพื่อให้พวกเขาหลบหนี แต่พารามิเตอร์ที่ไม่จำเป็นจะโดยปริยายหลบหนีเนื่องจาก((Int)->())?เป็นคำพ้องความหมายของOptional<(Int)->()>การปิดตัวเลือกจะหลบหนี


5
ได้รับ@escaping may only be applied to parameters of function type func doStuff(stuff: String, completion: (@escaping ()->())?) {
Lescai Ionel

1
a temporary solution is remove the @escaping attribute from optional function type, that keep the function as escaping. คุณช่วยอธิบายเพิ่มเติมได้ไหม? ความหมายเริ่มต้นใน swift 3 ไม่หนี ถึงแม้ว่ามันจะคอมไพล์โดยไม่ต้อง @escaping แต่ฉันก็กลัวว่ามันจะทำให้เกิดปัญหาโดยการไม่ถือว่าหนี นั่นไม่จริงเหรอ?
Pat Niemeyer

49
เมื่ออ่านเพิ่มเติมฉันเห็นว่า SR-2444 บอกว่าการปิดตัวเลือกทั้งหมดได้รับการปฏิบัติเหมือนการหลบหนีซึ่งเป็นข้อบกพร่องเสริม :) ฉันจะสมมติว่าเมื่อได้รับการแก้ไขคอมไพล์จะเตือนให้เราทำการเปลี่ยนแปลง
Pat Niemeyer

อาจจะเป็นหัวข้อเล็กน้อย แต่มันใช้งานได้@autoclosureอย่างไร หนึ่งได้รับข้อผิดพลาดเดียวกันที่นั่น ...
Gee.E

มันทำงานโดยไม่ต้องใช้ @escaping __ func doStuff (เนื้อหา: สตริง, การสำเร็จ: (() -> ())?) {
ФеннурМезитов

226

จาก: รายชื่อผู้รับจดหมายที่รวดเร็ว

โดยทั่วไป @escaping จะใช้ได้เฉพาะเมื่อปิดในตำแหน่งพารามิเตอร์ของฟังก์ชัน กฎ noescape โดยค่าเริ่มต้นจะใช้กับการปิดเหล่านี้ที่ตำแหน่งพารามิเตอร์ฟังก์ชั่นเท่านั้นมิฉะนั้นจะหายไป มวลรวมเช่น enums ที่มีค่าที่เกี่ยวข้อง (เช่นทางเลือก), tuples, structs เป็นต้นหากมีการปิดให้ปฏิบัติตามกฎเริ่มต้นสำหรับการปิดที่ไม่ได้อยู่ที่ตำแหน่งพารามิเตอร์ของฟังก์ชันเช่นกำลังหลบหนี

ดังนั้นฟังก์ชันพารามิเตอร์ทางเลือกคือ @escaping โดยค่าเริ่มต้น
@noeascape ใช้เฉพาะกับพารามิเตอร์ฟังก์ชันตามค่าเริ่มต้น


7
ฉันคิดว่านี่เป็นการเพิ่มข้อมูลที่สำคัญที่สุดให้กับเรื่องนี้ควรได้รับคำตอบ
Damian Dudycz

คำตอบที่สมเหตุสมผล มีความเป็นไปได้ที่จะเกิดการเปลี่ยนแปลงได้อย่างไร
GoldenJoe

2
สิ่งนี้สมเหตุสมผลเนื่องจากการพูดทางเทคนิค(()->Void)?เหมือนกับการบอกว่าคุณมีOptional<()->Void>และเพื่อที่Optionalจะรักษาความเป็นเจ้าของมันจะต้องยอมรับ@escapingฟังก์ชั่นเท่านั้น ฉันจะสามที่นี้ควรเป็นคำตอบที่ยอมรับ ขอบคุณ.
Dean Kelly

22

ฉันพบปัญหาที่คล้ายกันเนื่องจากการมิกซ์@escapingและไม่@escapingสับสนมากโดยเฉพาะถ้าคุณต้องผ่านการปิดรอบ

ฉันสิ้นสุดการกำหนดค่าเริ่มต้น no-op ให้กับพารามิเตอร์การปิด= { _ in }ซึ่งฉันคิดว่าเหมาะสม:

func doStuff(stuff: String = "do stuff",
        completion: @escaping (_ some: String) -> Void = { _ in }) {
     completion(stuff)
}

doStuff(stuff: "bla") {
    stuff in
    print(stuff)
}

doStuff() {
    stuff in
    print(stuff)
}

นี่สะอาด & ​​สวยในการนำไปใช้
Fred Faust

2
จะทำอย่างไรถ้าฉันต้องการตรวจสอบว่าบล็อกนี้ไม่มีข้อมูล / ว่างเปล่า?
Vyachaslav Gerchicov

2
คนเกียจคร้านนี่ใช้ไม่ได้กับวิธีโปรโตคอล "ไม่อนุญาตให้ใช้อาร์กิวเมนต์เริ่มต้นในวิธีโปรโตคอล" (Xcode 8.3.2)
Mike Taverne

17

ฉันทำให้มันทำงานใน Swift 3 โดยไม่มีการเตือนใด ๆ ด้วยวิธีนี้:

func doStuff(stuff: String, completion: (()->())? ) {
    print(stuff)
    action = completion
    completion?()
}

4

สิ่งสำคัญที่ต้องเข้าใจในตัวอย่างคือถ้าคุณเปลี่ยนActionไปสู่Action?การปิดกำลังหลบหนี ดังนั้นมาทำในสิ่งที่คุณเสนอ:

typealias Action = () -> ()

var action: Action? = { }

func doStuff(stuff: String, completion: Action?) {
    print(stuff)
    action = completion
    completion?()
}

ตกลงตอนนี้เราจะโทรหาdoStuff:

class ViewController: UIViewController {
    var prop = ""
    override func viewDidLoad() {
        super.viewDidLoad()
        doStuff(stuff: "do stuff") {
            print("swift 3!")
            print(prop) // error: Reference to property 'prop' in closure 
                        // requires explicit 'self.' to make capture semantics explicit
        }
    }
}

ความต้องการนั้นเกิดขึ้นเพียงเพื่อหลบหนีการปิด ดังนั้นการปิดกำลังหลบหนี นั่นเป็นเหตุผลที่คุณไม่ทำเครื่องหมายว่าหนีออกมา - มันกำลังหลบหนี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.