2015-09-22 13 views
6

Sau khi nâng cấp codebase của chúng tôi lên Swift2, tôi đã gặp sự cố bất thường. Thiết lập không phải là trừ đi cũng như đoàn kết như mong đợi.Swift 2.0 Đặt không hoạt động như mong đợi khi chứa lớp con NSObject

class A: NSObject { 
    let h: Int 

    init(h: Int) { 
     self.h = h 
    } 

    override var hashValue: Int { 
     return h 
    } 
} 

func ==(lhs: A, rhs: A) -> Bool { 
    return lhs.hashValue == rhs.hashValue 
} 

let a = A(h: 1) 
let b = A(h: 1) 

var sa = Set([a]) 
let sb = Set([b]) 

sa.subtract(sb).count // Swift1.2 prints 0, Swift 2 prints 1 

sa.contains(a) // Swift1.2 true, Swift 2 true 
sa.contains(b) // Swift1.2 true, Swift 2 false 

Có vẻ như Set mới không sử dụng hashValue cho các hoạt động nội bộ. Bất kỳ ý tưởng nào là một lỗi hay một cách giải quyết vấn đề này?

Trả lời

11

Tôi đã chơi với mã của bạn một chút. Tôi đã có thể để có được nó làm việc bằng cách không còn subclassing NSObject, nhưng thay vào đó phù hợp với các giao thức Hashable:

class A: Hashable { 
    let h: Int 

    init(h: Int) { 
     self.h = h 
    } 

    var hashValue: Int { 
     return h 
    } 

} 

func ==(lhs: A, rhs: A) -> Bool { 
    return lhs.hashValue == rhs.hashValue 
} 

let a = A(h: 1) 
let b = A(h: 1) 

var sa = Set([a]) 
let sb = Set([b]) 

sa.subtract(sb).count // Swift1.2 prints 0, Swift 2 prints 1 

sa.contains(a) // Swift1.2 true, Swift 2 true 
sa.contains(b) // Swift1.2 true, Swift 2 false 

a.hashValue == b.hashValue 

Khi bạn được thừa hưởng từ NSObject, quá tải == của bạn đã không thực sự được thực thi. Nếu bạn muốn điều này để làm việc với NSObject, bạn phải ghi đè isEquals:

override func isEqual(object: AnyObject?) -> Bool { 
    if let object = object as? A { 
     return object.h == self.h 
    } else { 
     return false 
    } 
} 
+0

Cảm ơn bạn! Tôi đã gặp vấn đề này với một lớp con MKAnnotation (cũng cần mở rộng NSObject). Bạn có liên kết đến một số tài liệu về chủ đề này không? – brki

+1

Cả hai isEqual và hashValue dường như là cần thiết để làm cho Đặt đúng với NSObject trong Swift 3 –

2
//: Playground - noun: a place where people can play 

import Foundation 

class X: NSObject { 
    var x:String 
    var y:Int 

    init(x:String, y:Int) { 
     self.x = x 
     self.y = y 
     super.init() 
    } 

    override var hashValue: Int { 
     return x.hashValue^y.hashValue 
    } 

    override func isEqual(object: AnyObject?) -> Bool { 
     if let rhs = object as? X { 
      return x == rhs.x && y == rhs.y 
     } else { 
      return false 
     } 
    } 
} 

func == (lhs:X, rhs:X) -> Bool { 
    return lhs.isEqual(rhs) 
} 

let x = X(x: "x1", y: 1) 
let y = X(x: "x1", y: 1) 
X(x: "x1", y: 1) == X(x: "x1", y: 1) // Swift 'x == y' (true) 
x.isEqual(y)       // Obj-C '[x isEqual: y]' (true) 

var s:Set<X> = [X(x: "x1", y: 1)] 
s.count // count == 1 
s.insert(X(x: "x2", y: 1)) 
s.count // count == 2 
s.insert(X(x: "x1", y: 1)) 
s.count // count == 2 
s.insert(X(x: "x2", y: 1)) 
s.count // count == 2 

tôi đã dành thời gian tìm kiếm câu trả lời đúng cho điều này cho đến khi tôi nhấn Câu hỏi này/trả lời. Tôi đã hoàn nguyên về cơ bản trong XCode Playground để xem điều gì đang xảy ra. Sử dụng các lớp con của NSObject trong Swift Set tạo một nhóm mã dễ đọc hơn.

+0

Tôi không biết tại sao rất nhiều người đã chọn câu trả lời khác. Đối với tôi, cái này là cái có giá trị và chính xác. Cảm ơn nhiều. –

+0

Chỉ cần đề cập đến, nó được yêu cầu trong 'NSObjectProtocol', nếu bạn ghi đè' func isEqual (_ object: Any?) -> Bool', bạn cũng phải ghi đè 'var hash: Int {get}'. Như trong tài liệu, nó nói 'Nếu hai đối tượng bằng nhau (được xác định bởi phương thức isEqual (_ :)), chúng phải có cùng giá trị băm. '. https://developer.apple.com/reference/objectivec/nsobjectprotocol/1418859-hash –

Các vấn đề liên quan