swift - NSObject उपवर्ग में स्विफ्ट: हैश बनाम हैशवैल, इसक्वल बनाम==




cocoa (2)

Hashable लागू करें, जिसके लिए आपको अपने प्रकार के लिए == ऑपरेटर को भी लागू करना होगा। ये स्विफ्ट मानक लाइब्रेरी में बहुत उपयोगी सामान के लिए उपयोग किए जाते हैं जैसे indexOf फ़ंक्शन जो केवल एक प्रकार के संग्रह पर काम करता है जो Equatable , या Set<T> प्रकार को लागू करता है जो केवल Equatable को लागू करने वाले प्रकारों के साथ काम करता है।

जब स्विफ्ट में NSObject को उपवर्गित कर रहा है, तो क्या आपको हैश को ओवरराइड करना चाहिए या हसबल को लागू करना चाहिए? इसके अलावा, क्या आप को अलग करना चाहिए: या लागू करना ==?


NSObject पहले से ही Hashable प्रोटोकॉल के अनुरूप है:

extension NSObject : Equatable, Hashable {
    /// The hash value.
    ///
    /// **Axiom:** `x == y` implies `x.hashValue == y.hashValue`
    ///
    /// - Note: the hash value is not guaranteed to be stable across
    ///   different invocations of the same program.  Do not persist the
    ///   hash value across program runs.
    public var hashValue: Int { get }
}

public func ==(lhs: NSObject, rhs: NSObject) -> Bool

मुझे कोई आधिकारिक संदर्भ नहीं मिला, लेकिन ऐसा लगता है कि hashValue ने NSObjectProtocol से hash विधि को कॉल किया, और == isEqual: पद्धति (उसी प्रोटोकॉल से) को कॉल करता है। जवाब के अंत में अपडेट देखें!

NSObject उपवर्गों के लिए, सही तरीका hash और isEqual: को ओवरराइड करने के लिए लगता है isEqual: और यहां एक प्रयोग है जो यह दर्शाता है कि:

1. hashValue और == ओवरराइड करें

class ClassA : NSObject {
    let value : Int

    init(value : Int) {
        self.value = value
        super.init()
    }

    override var hashValue : Int {
        return value
    }
}

func ==(lhs: ClassA, rhs: ClassA) -> Bool {
    return lhs.value == rhs.value
}

अब कक्षा के दो अलग-अलग उदाहरण बनाएं, जिन्हें "समान" माना जाता है और उन्हें एक सेट में रखा जाता है:

let a1 = ClassA(value: 13)
let a2 = ClassA(value: 13)

let nsSetA = NSSet(objects: a1, a2)
let swSetA = Set([a1, a2])

print(nsSetA.count) // 2
print(swSetA.count) // 2

जैसा कि आप देख सकते हैं, NSSet और Set दोनों वस्तुओं को अलग-अलग मानते हैं। यह वांछित परिणाम नहीं है। Arrays के अप्रत्याशित परिणाम भी हैं:

let nsArrayA = NSArray(object: a1)
let swArrayA = [a1]

print(nsArrayA.indexOfObject(a2)) // 9223372036854775807 == NSNotFound
print(swArrayA.indexOf(a2)) // nil

ब्रेकपॉइंट सेट करना या डीबग आउटपुट जोड़ना यह बताता है कि ओवरराइड == ऑपरेटर को कभी नहीं कहा जाता है। मुझे नहीं पता कि यह एक बग या इच्छित व्यवहार है।

2. ओवरराइड hash और isEqual:

class ClassB : NSObject {
    let value : Int

    init(value : Int) {
        self.value = value
        super.init()
    }

    override var hash : Int {
        return value
    }

    override func isEqual(object: AnyObject?) -> Bool {
        if let other = object as? ClassB {
            return self.value == other.value
        } else {
            return false
        }
    }
}

स्विफ्ट 3 के लिए isEqual: की परिभाषा isEqual: बदल गई

override func isEqual(_ object: Any?) -> Bool { ... }

अब सभी परिणाम अपेक्षित हैं:

let b1 = ClassB(value: 13)
let b2 = ClassB(value: 13)

let nsSetB = NSSet(objects: b1, b2)
let swSetB = Set([b1, b2])

print(swSetB.count) // 1
print(nsSetB.count) // 1

let nsArrayB = NSArray(object: b1)
let swArrayB = [b1]

print(nsArrayB.indexOfObject(b2)) // 0
print(swArrayB.indexOf(b2)) // Optional(0)

अद्यतन: व्यवहार को "कोको और उद्देश्य-सी" संदर्भ के साथ स्विफ्ट का उपयोग करते हुए ऑब्जेक्टिव-सी एपीआई के साथ बातचीत में प्रलेखित किया गया है:

NSObject वर्ग केवल एक पहचान तुलना करता है, इसलिए आपको अपनी खुद की isEqual: NSObject कक्षा से प्राप्त होने वाली कक्षाओं में विधि लागू करनी चाहिए।

अपने वर्ग के लिए समानता को लागू करने के भाग के रूप में, वस्तु तुलना में नियमों के अनुसार हैश संपत्ति को लागू करना सुनिश्चित करें।





cocoa