尝试使用Equatable协议中定义的'=='运算符比较AnyObject类型的两个对象会导致Swift中出现编译错误。有没有人找到比较这些对象的方法,却不知道可以用于向下转换的真实对象类型?
这个问题的背景是我有一个字典Dictionary< String,AnyObject>如果值应该通过下标提供,那么在某些时候我需要比较字典中的值以确保它们是唯一的。
修改的 这是一个展示问题的片段。
@objc(FooObject)
public class FooObject: NSManagedObject {
@NSManaged public var properties: Dictionary<String, AnyObject>
public subscript(property: String) -> AnyObject? {
get {
return properties[property]
}
set(newValue) {
for propertyValue in properties.values {
if propertyValue == newValue { // This line is not compiling: Cannot invoke '==' with AnyObject
println("Values in are expected to be unique!")
// Throw an exception here ...
}
}
properties[property] = newValue
}
}
}
注意类似于&lt; T:Equatable&gt;的泛型在类定义中声明并用作字典的值类型将无法解决问题,因为它不能与NSManagedObject子类一起使用。
答案 0 :(得分:20)
使用来自Swift文档的===运算符:
Swift还提供了两个标识运算符(===和!==),用于测试两个对象引用是否都引用同一个对象实例。
答案 1 :(得分:7)
我不认为这是可能的。问题是Equatable是用一个带
的方法定义的 func ==(a: T, b: T)
并且没有办法,在编译时,编译器可以找到正确的函数(因为它没有提前知道类型)。
你能做到这一点的唯一方法就是你对比较的类型有所了解。然后你可以具体地为每种类型调用适当的相等函数:
func compare(a: AnyObject, b: AnyObject) {
if let va = a as? Int, vb = b as? Int {if va != vb {return false}}
else if let va = a as? String, vb = b as? String {if va != vb {return false}}
else if let va = a as? Bool, vb = b as? Bool {if va != vb {return false}}
...
else {
// not a type we expected
return false;
}
}
有趣的是,例如,C#通过将IComparable
接口定义为具有方法来解决这个问题:
int CompareTo(Object obj)
这允许每个IComparable
对象将自己与任何其他对象进行比较(但函数必须始终在此处进行自己的类型检查,以确保obj
为正确的类型)。
答案 2 :(得分:3)
如果对象属于===
类型,则Swift 3无法与Any
一起进行参考比较。原因是Any
可能包含整数和浮点数或任何其他事实上不是对象的东西。因此,您必须将其强制转换为NSObject
才能比较2个项目。像
if (obj1 as! NSObject) === (obj2 as! NSObject)
{
//logic goes here
}
答案 3 :(得分:1)
查看实施:
/// Returns true if these arrays contain the same elements.
func ==<T : Equatable>(lhs: [T], rhs: [T]) -> Bool
这意味着编译器需要知道左侧和右侧参数必须属于同一类型。因此,您正在实现的功能应如下所示:
func compareFunc <T: Equatable>(par1: T, par2: T) {
...
if par1 == par2 {
...
}
...
}
编辑:
你的词典应该是这样的:
func compareFunc <T: Equatable>(dic1: [String : T], dic2: [String : T]) {
...
if dic1[yourKey] == dic2[yourKey] {
...
}
...
}
编辑2:
一个例子:
func compareFunc <T: Equatable>(dic1: [String : T], dic2 : [String : T]) -> Bool {
return dic1["key"] == dic2["key"]
}
compareFunc(["key": "value"], ["key": "value"])
答案 4 :(得分:1)
对于大多数用例,这应该很有效:
func equalAny<BaseType: Equatable>(lhs: Any, rhs: Any, baseType: BaseType.Type) -> Bool {
guard
let lhsEquatable = lhs as? BaseType,
let rhsEquatable = rhs as? BaseType
else { return false }
return lhsEquatable == rhsEquatable
}
var a: Any = "a" as Any
var b: Any = "b" as Any
equalAny(lhs: a, rhs: b, baseType: NSObject.self) // false
b = "a"
equalAny(lhs: a, rhs: b, baseType: NSObject.self) // true
a = CGFloat(5)
b = 5
equalAny(lhs: a, rhs: b, baseType: NSObject.self) // true
a = ["hello": "world"]
b = ["hello": "world"]
equalAny(lhs: a, rhs: b, baseType: NSObject.self) // true
答案 5 :(得分:1)
如果您想在Objective-C中使用此功能,为什么不将properties
设为Dictionary<String, NSObject>
?毕竟,NSObject
有isEqual
,您无法将原始类型放入NSDictionary
。
所以它看起来像这样:
@objc(FooObject)
public class FooObject: NSManagedObject {
@NSManaged public var properties: Dictionary<String, NSObject>
public subscript(property: String) -> NSObject? {
get {
return properties[property]
}
set(newValue) {
for propertyValue in properties.values {
if propertyValue == newValue { // This line is not compiling: Cannot invoke '==' with AnyObject
print("Values in are expected to be unique!")
// Throw an exception here ...
}
}
properties[property] = newValue
}
}
}
答案 6 :(得分:0)
尝试使用'=='比较AnyObject类型的两个对象 Equatable协议中定义的运算符导致编译错误 迅速。有没有人找到一种方法来主持这些对象,而不知道 可以用于向下投射的真实物体类型?
问题是,你如何比较两个任意对象的内容相等性?它没有一般定义。如果对象是NSObject
或至少NSObjectProtocol
的实例,那么有isEqual:
,但对于不是的对象,你怎么能这样做?
答案 7 :(得分:0)
当我需要比较不相等但内容相同的值时,我会使用这些方法。神奇的是比较封闭。当你想要专注于其他事情而不是让每个阶级都相等时,写起来真的很简单。
/*Pre requsite: Temp and AnotherTemp must be string convertible*/
let a:AnyObject = "1"
let b:AnyObject = Temp("2")
let b:AnyObject = AnotherTemp(3)
let arr:[AnyObject] = [a,b,c]
Utils.has(arr,2,{"\($0)") == $1})//true or false
class Utils{
/**
* EXAMPLE: ["a","b","c"].has("b",{$0 == $1})//true
*/
static func has<T,V>(_ variables:[T],_ match:V,_ method:(T,V)->Bool) -> Bool where V:Equatable{
return first(variables, match, method) != nil
}
/**
* Think of this method as: firstOccurence of something
* Returns the first item that matches PARAM: match according to the constraints in PARAM: method
* EXAMPLE: ["a","b","c"].first("b",{$0 == $1})//b
* EXAMPLE: [("a",0),("b",1)].first("b",{$0.0 == $1}).1//b
* EXAMPLE: [(id:"a",val:0),(id:"b",val:1)].first("b",{$0.id == $1}).val//b
* NOTE: This method should have an extension, but making an extension for two generic variables proved difficult, more research needed, for now use the ArrayParser.first method call
* NOTE: you could do: arr.forEach{/*assert logic goes here*/} but forEach can't return early so you are forced to iterate the entire list
*/
static func first<T,V>(_ variables:[T],_ match:V,_ method:(T,V)->Bool) -> T? where V:Equatable{
for item in variables{
if(method(item,match)){
return item
}
}
return nil
}
}
答案 8 :(得分:0)
我甚至有点尴尬甚至暗示这一点,但我也遇到了这个问题。我的情况是我正在比较源于Dictionaries
的JSON,它们实际上生活在2个不同的NSManagedObjectContexts
中,因此对象不会相同,尽管值可能是。
无论如何,我所做的只是从任何值创建字符串并比较它们。它是一个黑客攻击,但在我的情况下是出于测试目的而且有效。
set(newValue) {
for propertyValue in properties.values {
if String(describing:propertyValue) == String(describing:newValue){
println("Values in are expected to be unique!")
// Throw an exception here ...
}
}
properties[property] = newValue
}