วัตถุ X ของคลาส Y ไม่ใช้ methodSignatureForSelector ใน Swift


89

ฉันมีคลาสบุคคลที่สร้างอินสแตนซ์หลายครั้งแต่ละคนจะมีเวลาของตัวเอง เมื่อในของฉันinitสำหรับฉันโทรPersonstartTimer()

class Person {
 var timer = NSTimer()
 func startTimer() {
    timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("timerTick"), userInfo: nil, repeats: true)
 }

 func timerTick() {
    angerLevel++
    println("Angry! \(angerLevel)")
 }
...
...
}

ดังนั้นฉันอาจมีบุคคล 3 อินสแตนซ์ในอาร์เรย์ของPerson[]. ฉันได้รับข้อผิดพลาด:

2014-06-25 13:57:14.956 ThisProgram[3842:148856] *** NSForwarding: warning: object 0x113760048 of class '_TtC11ThisProgram6Person' does not implement methodSignatureForSelector: -- trouble ahead

ฉันอ่านที่อื่นว่าฉันควรได้รับมรดกจากNSObjectแต่นี่คือใน Swift ไม่ใช่ Obj-C ฟังก์ชันนี้อยู่ในคลาสดังนั้นฉันไม่แน่ใจว่าต้องทำอย่างไร


4
คุณคิดแล้วเห็นว่าชั้นควรสืบทอดจาก class Person : NSObject { ... }NSObject: คุณกำลังมองหาวิธีแก้ปัญหาอื่นอยู่หรือไม่?
Martin R

คำตอบ:


160

อย่าคิดว่าNSObjectเป็นคลาส Objective-C ให้คิดว่าเป็นคลาส Cocoa / Foundation แม้ว่าคุณจะใช้ Swift แทน Objective-C แต่คุณก็ยังใช้เฟรมเวิร์กเดียวกันทั้งหมด

สองตัวเลือก: (1) เพิ่มdynamicแอตทริบิวต์ให้กับฟังก์ชันที่คุณต้องการอ้างอิงเป็นตัวเลือก:

    dynamic func timerTick() {
        self.angerLevel++
        print("Angry! \(self.angerLevel)")
    }

หรือ (2) ประกาศPersonเป็นคลาสย่อยจากNSObjectนั้นโทรไปsuper.init()ที่จุดเริ่มต้นของ initializer:

class Person: NSObject {
    var timer = NSTimer()
    var angerLevel = 0

    func startTimer() {
        print("starting timer")
        timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerTick", userInfo: nil, repeats: true)
    }

    func timerTick() {
        self.angerLevel++
        print("Angry! \(self.angerLevel)")
    }

    override init() {
        super.init()
        self.startTimer()
    }
}

3
คุณควรจะตกแต่งการประกาศฟังก์ชันเช่นนี้@objc func timerTick()ได้ด้วย NSTimer API ดูเหมือนจะขึ้นอยู่กับ Obj-C Runtime
macshome

โทรดี - เพิ่มคำตอบ
Nate Cook

1
ขอบคุณที่แก้ไขปัญหาของฉัน แต่คุณอธิบายได้ไหมว่าทำไม? ส่วน @objc ต้องการอะไร
ผู้รุกราน

NSTimerใช้การส่งต่อข้อความเพื่อเรียกตัวเลือกเป้าหมายซึ่งเป็นคุณสมบัติ Objective-C ที่ไม่ได้จัดการกับประเภท Swift ตามค่าเริ่มต้น เมื่อคุณใช้@objcแอตทริบิวต์หรือสืบทอดจากคลาส Objective-C คุณกำลังเลือกใช้คุณลักษณะต่างๆรวมถึงการส่งต่อข้อความ
Nate Cook

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

32

ตั้งแต่ XCode6 beta 6 คุณสามารถใช้ func 'ไดนามิก'

dynamic func timerTick() { .... }

สิ่งนี้ช่วยแก้ปัญหาของฉันในการพยายามใช้ UILocalizedIndexedCollation.currentCollation ()
DogCoffee

นี่เป็นแนวทางที่ดีกว่าเมื่อเทียบกับการทำให้คลาสทั้งหมดสืบทอดจาก NSObject
bobics

8

ฉันมีข้อผิดพลาดที่คล้ายกันในการพยายามใช้โดย let encodedArchive = NSKeyedArchiver.archivedDataWithRootObject(archive) as NSDataที่ไฟล์เก็บถาวรเป็นอาร์เรย์ของคลาสที่กำหนดเอง ฉันพบว่าการประกาศคลาสที่กำหนดเองนั้นเป็นคลาสย่อยของ NSObject และ NSCoding นั้นเป็นเคล็ดลับ จะต้องใช้อีกสองสามบรรทัดเพื่อให้สอดคล้องกับโปรโตคอลของ NSCoding จึงจะมีลักษณะดังนี้เริ่มต้นด้วย:

class Person: NSObject, NSCoding {
  init() {
    super.init()
  }

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