比较Swift中的AnyObjects,而不将它们转换为特定类型

时间:2014-09-17 22:06:17

标签: swift comparison

尝试使用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子类一起使用。

9 个答案:

答案 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>?毕竟,NSObjectisEqual,您无法将原始类型放入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
}