ฉันจะจัดการกับการเลิกใช้การอนุมาน @objc ด้วย #selector () ใน Swift 4 ได้อย่างไร


131

ฉันกำลังพยายามแปลงซอร์สโค้ดของโปรเจ็กต์จาก Swift 3 เป็น Swift 4 คำเตือน Xcode หนึ่งคำเตือนที่ให้ฉันเป็นเรื่องเกี่ยวกับตัวเลือกของฉัน

ตัวอย่างเช่นฉันเพิ่มเป้าหมายลงในปุ่มโดยใช้ตัวเลือกปกติดังนี้:

button.addTarget(self, action: #selector(self.myAction), for: .touchUpInside)

นี่คือคำเตือนที่แสดง:

อาร์กิวเมนต์ของ '#selector' หมายถึงวิธีอินสแตนซ์ 'myAction ()' ใน 'ViewController' ที่ขึ้นอยู่กับการอนุมานแอตทริบิวต์ '@objc' ที่เลิกใช้งานใน Swift 4

เพิ่ม '@objc' เพื่อแสดงวิธีการอินสแตนซ์นี้กับ Objective-C

ตอนนี้การกดปุ่มFixข้อความแสดงข้อผิดพลาดทำกับฟังก์ชันของฉัน:

// before
func myAction() { /* ... */ }

// after
@objc func myAction() { /* ... */ }

ฉันไม่ต้องการเปลี่ยนชื่อฟังก์ชั่นทั้งหมดของฉันเพื่อรวม@objcเครื่องหมายและฉันคิดว่าไม่จำเป็น

ฉันจะเขียนตัวเลือกใหม่เพื่อจัดการกับการเลิกใช้งานได้อย่างไร


คำถามที่เกี่ยวข้อง:


3
Nope เครื่องหมายพวกเขาเป็น@objc คือตอนนี้จำเป็นเพื่อที่จะให้พวกเขาที่จะ Obj-C และดังนั้นจึงใช้กับตัวเลือก
Hamish

3
ดังนั้นส่วนที่เลิกใช้งานจึงอนุมานฟังก์ชันการเข้าถึงสาธารณะเป็น@objc? มันค่อนข้างน่ารำคาญ แต่โดยทั่วไปแล้วฉันจะทำให้ฟังก์ชันเหล่านี้เป็นแบบส่วนตัวทำให้ฉันต้องทำเครื่องหมายว่ามันเป็นยัง@objcไงก็ได้
Connor

2
ดูSE-0160สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลง อีกทางเลือกหนึ่งคือการทำเครื่องหมายชั้นเรียนที่คุณกำหนด@objcMembersเพื่อเปิดเผยสมาชิกที่เข้ากันได้กับ Obj-C ทั้งหมดให้กับ Obj-C แต่ฉันจะไม่แนะนำอย่างนั้นเว้นแต่คุณจะต้องการให้ทั้งชั้นเปิดเผยจริงๆ
Hamish

3
@LinusGeffarth ตามที่กล่าวไว้ในข้อเสนอการเพิ่มขนาดของการเชื่อมโยงไบนารีและไดนามิกโดยไม่จำเป็นจะใช้เวลานานขึ้น ฉันไม่คิดว่ามันจะยุ่งยากเกินไปสำหรับความชัดเจนที่เพิ่มขึ้นซึ่งคุณหมายถึงโดยเฉพาะสำหรับบางสิ่งที่จะใช้จาก Obj-C
Hamish

1
พยายามแล้วไม่ทำงาน
LinusGeffarth

คำตอบ:


156

การแก้ไขนั้นถูกต้อง - ไม่มีอะไรเกี่ยวกับตัวเลือกที่คุณสามารถเปลี่ยนแปลงได้เพื่อให้วิธีการที่อ้างถึงการสัมผัสกับ Objective-C

เหตุผลทั้งหมดสำหรับคำเตือนนี้ในครั้งแรกที่เป็นผลมาจากSE-0160 ก่อนหน้า Swift 4 internalหรือสูงกว่าสมาชิกที่เข้ากันได้กับ Objective-C ของNSObjectคลาสที่สืบทอดถูกอนุมานว่าเป็น@objcดังนั้นจึงสัมผัสกับ Objective-C ดังนั้นจึงอนุญาตให้เรียกใช้โดยใช้ตัวเลือก (เนื่องจากรันไทม์ Obj-C เป็นสิ่งจำเป็นเพื่อค้นหาวิธีการ การใช้งานสำหรับตัวเลือกที่กำหนด)

อย่างไรก็ตามใน Swift 4 ไม่เป็นเช่นนั้นอีกต่อไป เฉพาะประกาศที่เฉพาะเจาะจงมากจะสรุปตอนนี้จะ@objcยกตัวอย่างเช่นการแทนที่ของ@objcวิธีการใช้งานของ@objcความต้องการโปรโตคอลและการประกาศที่มีลักษณะที่บ่งบอกถึงเช่น@objc@IBOutlet

แรงจูงใจเบื้องหลังสิ่งนี้ตามรายละเอียดในข้อเสนอที่เชื่อมโยงข้างต้นประการแรกคือเพื่อป้องกันไม่ให้เมธอดโอเวอร์โหลดในการNSObjectสืบทอดคลาสไม่ให้ชนกันเนื่องจากมีตัวเลือกที่เหมือนกัน ประการที่สองช่วยลดขนาดไบนารีโดยไม่ต้องสร้าง thunks สำหรับสมาชิกที่ไม่จำเป็นต้องสัมผัสกับ Obj-C และประการที่สามช่วยเพิ่มความเร็วของการเชื่อมโยงแบบไดนามิก

หากคุณต้องการให้สมาชิกเห็น Obj-C คุณต้องทำเครื่องหมายเป็น@objcตัวอย่างเช่น:

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        button.addTarget(self, action: #selector(foo), for: .touchUpInside)
    }

    @objc func foo() {
       // ... 
    }
}

(ผู้ย้ายข้อมูลควรทำสิ่งนี้ให้คุณโดยอัตโนมัติด้วยตัวเลือกเมื่อเรียกใช้โดยเลือกตัวเลือก "ย่อขนาดการอนุมาน")

ในการเปิดเผยกลุ่มสมาชิกต่อ Obj-C คุณสามารถใช้@objc extension:

@objc extension ViewController {

    // both exposed to Obj-C
    func foo() {}
    func bar() {}
}

สิ่งนี้จะเปิดเผยสมาชิกทั้งหมดที่กำหนดไว้ใน Obj-C และให้ข้อผิดพลาดกับสมาชิกใด ๆ ที่ไม่สามารถสัมผัสกับ Obj-C ได้ (เว้นแต่จะระบุไว้อย่างชัดเจนว่าเป็น@nonobjc)

ถ้าคุณมีชั้นเรียนที่คุณต้องการทั้งหมดสมาชิก Obj-C เข้ากันได้กับได้สัมผัสกับ Obj-C คุณสามารถทำเครื่องหมายชั้นเป็น@objcMembers:

@objcMembers
class ViewController: UIViewController {
   // ...
}

ตอนนี้สมาชิกทั้งหมดที่สามารถอนุมานได้@objcจะเป็น อย่างไรก็ตามฉันไม่แนะนำให้ทำเช่นนี้เว้นแต่คุณจะต้องการให้สมาชิกทุกคนสัมผัสกับ Obj-C เนื่องจากข้อเสียดังกล่าวข้างต้นของการเปิดเผยสมาชิกโดยไม่จำเป็น


2
เหตุใด Xcode จึงไม่ทำเช่นนั้นโดยอัตโนมัติเมื่อแปลงรหัสเป็นไวยากรณ์ล่าสุด
LinusGeffarth

1
ฉันสงสัยว่าเป็นไป@IBActionโดยอัตโนมัติ@objcหรือไม่? จนถึงตอนนี้ยังไม่เป็นเช่นนั้น แต่มันจะมีเหตุผล แก้ไข: NVM ข้อเสนออย่างชัดเจนระบุว่าเพียงพอสำหรับวิธีการที่จะเป็น@IBAction @objc
Sulthan

8
ฉันไม่เข้าใจเรื่องนี้ ฉันไม่มีรหัส Objective-C อะไรเลย ไม่มีวิธีเพิ่มเป้าหมายให้กับปุ่มอย่างรวดเร็วอย่างรวดเร็วหรือไม่? หรือใช้ตัวเลือก? ฉันไม่ต้องการให้รหัสของฉันยุ่งกับแอตทริบิวต์นี้ เป็นเพราะ UIButton มาจาก NSObject / Obj-c-thing หรือไม่?
Sti

11
@Sti So ตอบโดยตรงว่า " ไม่มีวิธีเพิ่มเป้าหมายให้กับปุ่ม Swift อย่างรวดเร็วหรือใช้ตัวเลือก " - ไม่ไม่มีวิธี "Pure Swift" ในการใช้ตัวเลือกเพื่อส่งไปยังวิธีการ พวกเขาพึ่งพารันไทม์ Obj-C เพื่อค้นหาการใช้งานวิธีการเพื่อเรียกตัวเลือกเฉพาะ - รันไทม์ของ Swift ไม่มีความสามารถนั้น
Hamish

12
ตัวเลือกผู้ชายเป็นระเบียบ ดูเหมือนว่าการอัปเดตที่รวดเร็วอื่น ๆ ที่พวกเขากำลังยุ่งอยู่กับมัน เหตุใดเราจึงไม่มีตัวเลือกการเติมข้อความอัตโนมัติที่รวดเร็วเพียงอย่างเดียวสำหรับวิธีการ ทำให้โค้ดดูน่าเกลียดมาก
John Riselvato

16

ในฐานะที่เป็นเอกสารแอปเปิ้ลอย่างเป็นทางการ คุณต้องใช้ @objc เพื่อเรียกใช้ Selector Method

ใน Objective-C ตัวเลือกคือประเภทที่อ้างถึงชื่อของเมธอด Objective-C ใน Swift ตัวเลือก Objective-C จะแสดงโดยSelectorโครงสร้างและสามารถสร้างได้โดยใช้#selector นิพจน์ ในการสร้างตัวเลือกสำหรับวิธีการที่สามารถเรียกใช้จาก Objective-C ให้ส่งชื่อของเมธอดเช่น #selector(MyViewController.tappedButton(sender:)). ในการสร้างตัวเลือกสำหรับเมธอด Objective-C getter หรือ setter ของคุณสมบัติให้ส่งชื่อคุณสมบัติที่นำหน้าโดยgetter:หรือsetter:เลเบลเช่น #selector(getter: MyViewController.myButton).


ทั้งหมดที่ฉันเข้าใจจากสิ่งนี้ก็คือในบางกรณีฉันต้องเพิ่ม @objc ในจุดเริ่มต้นของ func ไม่ว่าฉันจะเรียกมันจาก Objective-C หรือไม่ก็ตาม ฉันเพิ่งเรียนรู้ Swift โดยใช้ Obj-C มาหลายปี
SundialSoft

10

สำหรับฉันคิดว่า Swift 4.2 สิ่งที่คุณต้องทำคือกำหนด @IBAction ให้กับวิธีการของคุณและคุณสามารถหลีกเลี่ยงคำอธิบายประกอบ @objc โง่ ๆ นี้ได้

`` `

let tap  =  UITapGestureRecognizer(target: self, action: #selector(self.cancel))


@IBAction func cancel()
{
    self.dismiss(animated: true, completion: nil)
}

1
สิ่งนี้จะบอกผู้สร้างอินเทอร์เฟซว่าสามารถเชื่อมต่อฟังก์ชันนี้ได้ซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ มันทำแบบเดียวกับ @objc ด้วย
Josh Paradroid

2

ดังที่ได้กล่าวไปแล้วในคำตอบอื่น ๆ ไม่มีวิธีใดที่จะหลีกเลี่ยง@objcคำอธิบายประกอบสำหรับตัวเลือกได้

แต่คำเตือนที่กล่าวถึงใน OP สามารถปิดเสียงได้โดยทำตามขั้นตอนต่อไปนี้:

  1. ไปที่การตั้งค่าการสร้าง
  2. ค้นหาคีย์เวิร์ด@objc
  3. ตั้งค่าของอินเทอร์เฟซ Swift 3 @objcเป็นOff

ด้านล่างนี้คือภาพหน้าจอที่แสดงขั้นตอนดังกล่าวข้างต้น:

ปิดเสียงเตือน "อินเทอร์เฟซ Swift 3 @objc"

หวังว่านี่จะช่วยได้


สิ่งนี้ส่งผลต่อโครงการของคุณในภาพรวมอย่างไร
Zonily Jame

1
ขนาด * .ipa หรือ * .xarchive และเวลาในการสร้างเป็นอย่างไร?
Zonily Jame

@ZonilyJame ฉันไม่ได้สังเกตเวลาสร้าง แต่ไม่มีผลกับขนาด IPA
S1LENT WARRIOR

นี่ไม่ใช่ค่าที่แนะนำที่คุณสามารถใช้ได้หากคุณใช้เวอร์ชัน 4.0+ ที่รวดเร็วตามหลักเกณฑ์ของ Apple ตรวจสอบhelp.apple.com/xcode/mac/current/#/deve838b19a1
Bhavin_m

1
คุณสามารถอัปเดตคำตอบนี้ได้โดยตั้งค่าเป็นค่าเริ่มต้นใน Xcode 11 สิ่งสำคัญคือต้องตั้งค่าทั้งในโครงการและเป้าหมายของคุณเพื่อลบคำเตือนอย่างสมบูรณ์
ทอมมีซี

2

หากคุณต้องการสมาชิก c วัตถุประสงค์ในตัวควบคุมมุมมองของคุณเพียงแค่เพิ่ม @objcMembersที่ด้านบนของตัวควบคุมมุมมอง และคุณสามารถหลีกเลี่ยงปัญหานี้ได้โดยการเพิ่ม IBAction ในรหัสของคุณ

@IBAction func buttonAction() {

}

ตรวจสอบให้แน่ใจว่าได้เชื่อมต่อเต้าเสียบนี้ในสตอรีบอร์ด

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