Сравните AnyObjects в Swift, не приводя их к определенному типу
Попытка сравнить два объекта типа AnyObject с помощью оператора '==', определенного в протоколе Equatable, приводит к ошибке компиляции в Swift. Кто-нибудь нашел способ сравнить такие объекты, не зная реального типа объектов, которые можно использовать для даункинга?
Предпосылкой для этого вопроса является то, что у меня есть словарь Dictionary
РЕДАКТИРОВАТЬ Вот фрагмент, демонстрирующий проблему.
@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
}
}
}
Обратите внимание, что универсальный тип, такой как
9 ответов
Используйте оператор === из документации Swift:
Swift также предоставляет два идентификатора оператора (=== и!==), которые вы используете для проверки, ссылаются ли обе ссылки на один и тот же экземпляр объекта.
Я не думаю, что это возможно. Проблема в том, что 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
имеет правильный тип).
Swift 3 не работает с ===
для сравнения сравните, если объекты имеют тип Any
, Причина в Any
может содержать целое число и число с плавающей запятой или любую другую вещь, которая на самом деле не является объектом. Так что вам придется бросить его NSObject
сравнить 2 предмета. подобно
if (obj1 as! NSObject) === (obj2 as! NSObject)
{
//logic goes here
}
Это должно работать довольно хорошо для большинства случаев использования:
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
Посмотрите на реализацию:
/// 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"])
Если вы хотите использовать это в 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
}
}
}
Я немного стесняюсь даже предлагать это, но я тоже столкнулся с этим. Мой сценарий состоял в том, что я сравнивал JSON-источник Dictionaries
а они ведь жили в 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
}
Я использую эти методы, когда мне нужно сравнить значения, которые не равны, но их содержание есть. Волшебство в закрытии сравнения. Очень просто написать, когда вы хотите сосредоточиться на других вещах, а не на том, чтобы сделать каждый класс уравновешенным.
/*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
}
}
Попытка сравнить два объекта типа AnyObject с помощью оператора '==', определенного в протоколе Equatable, приводит к ошибке компиляции в Swift. Кто-нибудь нашел способ конкурировать с такими объектами, не зная реальный тип объектов, которые могут быть использованы для броска вниз?
Проблема в том, как вы сравниваете два произвольных объекта на предмет равенства контента? Там нет общего определения этого. Если объекты были экземплярами NSObject
, или по крайней мере NSObjectProtocol
то есть isEqual:
, но для объектов, которые не являются, как вы можете это сделать?