การประกาศไม่สามารถเป็นข้อผิดพลาดทั้ง 'สุดท้าย' และ 'ไดนามิก' ใน Swift 1.2


123

คำประกาศvalueด้านล่าง

import Foundation

class AAA: NSObject {
    func test2() {
        self.dynamicType
    }
}
extension AAA {
    static let value    =   111
}

ทำให้เกิดข้อผิดพลาดในการคอมไพล์ต่อไปนี้

A declaration cannot be both 'final' and 'dynamic'

เหตุใดจึงเกิดขึ้นและฉันจะจัดการกับสิ่งนี้ได้อย่างไร

ฉันใช้ Swift 1.2 (เวอร์ชันที่จัดส่งภายใน Xcode 6.3.1 6D1002)


func test2ประกาศไม่จำเป็นต้องเกิดข้อผิดพลาดที่เป็นของ Xcode 7.3.1
rob mayoff


เพียงใส่ตัวแปรคงที่ในโครงสร้างการตั้งชื่ออื่นที่ดีกว่า
onmyway133

คำตอบ:


224

ปัญหานี้เกิดขึ้นเพราะสวิฟท์พยายามที่จะสร้างการเข้าถึงแบบไดนามิกสำหรับคุณสมบัติคงที่สำหรับ Obj-C NSObjectความเข้ากันได้ตั้งแต่ระดับสืบทอดจาก

หากโปรเจ็กต์ของคุณอยู่ใน Swift เท่านั้นแทนที่จะใช้varAccessor คุณสามารถหลีกเลี่ยงปัญหาผ่าน@nonobjcแอตทริบิวต์ใน Swift 2.0:

import Foundation

class AAA: NSObject {}
extension AAA {
    @nonobjc static let value = 111
}

โครงการของฉันมีไฟล์ Objective-C บางไฟล์ แต่ไม่มีโค้ดใดที่โต้ตอบกับอินสแตนซ์ของคลาสนี้ ( AAAที่นี่) ดังนั้นฉันเดาว่าฉันเข้าใจแล้วใช่ไหม
Nicolas Miari

นี่ควรเป็นคำตอบที่เลือกหากใช้ Swift codebase ล้วนๆ
idzski

ฉันพยายามเพิ่ม vars แบบคงที่ (คลาส) ให้กับNSManagedObjectคลาสย่อย สิ่งนี้แก้ไขได้!
Nicolas Miari

ฉันเป็นคนเดียวที่พบการแก้ไขนี้เพื่อทำให้ SourceKitService สำหรับ Xcode 7.3 สมบูรณ์หรือไม่
NoodleOfDeath

57

คุณจะได้รับข้อผิดพลาดนี้หากชั้นเรียนของคุณเป็นไปตามเงื่อนไขเหล่านี้

  • คลาสย่อยจากNSObject.
  • มีstatic letสนาม
  • dynamicTypeเข้าถึงข้อมูลจากวิธีการเช่นผ่านทาง

ฉันไม่รู้ว่าเหตุใดจึงเกิดขึ้น แต่คุณสามารถลองวิธีแก้ปัญหานี้ได้

static var value: Int {
    get {
        return 111
    }
}

หรือในรูปแบบที่สั้นกว่า

static var value: Int {
    return 111
}

ใช้static var { get }แทนstatic let.


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

หากคุณกังวลเกี่ยวกับต้นทุนการคำนวณมูลค่าดังกล่าวคุณสามารถสร้างได้ครั้งเดียวและแคชเช่นนี้

static var value: Int {
    return cache
}
private let cache = getTheNumber()

หรือเช่นนี้หากคุณต้องการซ่อนการมีอยู่ของแคชอย่างสมบูรณ์

static var value: Int {
    struct Local {
        static let cache = getTheNumber()
    }
    return Local.cache
}

5
สิ่งนี้ก่อให้เกิดคุณสมบัติที่คำนวณซึ่งจะถูกคำนวณใหม่ทุกครั้งที่เข้าถึง สำหรับกรณีนี้อาจไม่สำคัญมากนัก แต่ฉันคิดว่ามันคุ้มค่าที่จะกล่าวถึงดังนั้นจึงไม่มีใครใช้วิธีแก้ปัญหานี้สำหรับวัตถุที่ใหญ่กว่า
Nick Podratz

@NickPodratz นี่จะเป็นคุณสมบัติที่คำนวณได้ด้วยหรือไม่? private static let _value: Int = 111 static var value: Int { return _value }มันไม่มีget {แต่คอมไพเลอร์กล่าวถึงบางสิ่งเกี่ยวกับคุณสมบัติที่คำนวณได้ถ้าฉันใช้varแทนlet
แฮช

1
@ ยิ่งใหญ่กว่านั้น ภายในวงเล็บปีกกาคุณสร้างการปิดgetในกรณีนี้โดยปริยาย let value: Int = { return 111 }()คุณสามารถทำอะไรแทนคือกำหนดผลของการปิดให้ตัวแปรเพื่อให้ปิดที่เรียกว่าเพียงครั้งเดียวไปนี้: วงเล็บด้านท้ายเรียกการปิด แต่โปรดทราบว่านี่เป็นคุณสมบัติที่เก็บไว้อีกครั้งดังนั้นจึงไม่มีในส่วนขยาย
Nick Podratz

เห็นด้วยกับการประเมินของ @NickPodratz แม้ว่าสิ่งนี้จะแก้ไขข้อผิดพลาดที่ OP กล่าวถึงและทำให้เป็นคำตอบที่ถูกต้อง แต่ก็ไม่ได้ให้ประโยชน์ใด ๆ หากคุณต้องการให้ตัวแปรของคุณเป็นแบบคงที่ (ซึ่งดูเหมือนจะเป็นประเด็น) คำตอบของ Alex ดีกว่าในกรณีนั้น (สมมติว่า Swift บริสุทธิ์)
Matt Long

18

ฉันมีข้อผิดพลาดนี้เช่นกัน

ปัญหาของฉันเป็นเพียงตัวแปรคงที่ในส่วนขยายที่รวดเร็ว

extension NotificationsViewController: UITableViewDataSource , UITableViewDelegate {

    static var timeIntervalFormatter = NSDateComponentsFormatter()

}

การย้ายไปใช้ชั้นเรียนช่วยแก้ปัญหาให้ฉันได้


7

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

คลาสสุดท้ายที่แทนที่ตัวแปรคำนวณที่กำหนดในส่วนขยายทำให้เกิดข้อผิดพลาดนี้เช่นกัน มันใช้งานได้กับฟังก์ชันและดูเหมือนบั๊กของคอมไพเลอร์

// at line 0: a declaration cannot be both 'final' and 'dynamic'

import UIKit

extension UIViewController {
    var test: Int { return 0 }
}

final class TestController: UIViewController {
    override var test: Int { return 1 }
}

7

ฉันแก้ไขปัญหานี้โดยย้ายการประกาศแบบคงที่ไปยังโครงสร้างใหม่ที่ฉันกำหนดไว้ในส่วนขยาย

แทนสิ่งนี้:

extension NSOperationQueue {
    static var parsingQueue : NSOperationQueue = {
        let queue = NSOperationQueue()
        queue.maxConcurrentOperationCount = 1
        return queue
        }()
}

ฉันมีสิ่งนี้:

extension NSOperationQueue {        
    struct Shared {
        static var parsingQueue : NSOperationQueue = {
            let queue = NSOperationQueue()
            queue.maxConcurrentOperationCount = 1
            return queue                
            }()
    }
}

0

คุณสามารถทำเครื่องหมายเป็นส่วนตัวเพื่อป้องกันข้อผิดพลาดนี้ หากคุณต้องการเปิดเผยคุณสามารถรวมไว้ในฟังก์ชันสาธารณะ:

extension AAA {

    private static let value = 111

    public func getDatValue() -> Int {
        return AAA.value
    }    
}

ในกรณีของฉันฉันอ้างถึงคุณสมบัติในส่วนขยายเท่านั้นดังนั้นจึงไม่จำเป็นต้องเปิดเผย


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