Самый элегантный способ сравнить два варианта в Swift

У меня есть две переменные AnyObject?, которые я хотел бы сравнить для ссылочного равенства:

var oldValue: AnyObject?
var newValue: AnyObject?
...
if oldValue != newValue {
    changed = true
}

Это не работает, потому что я, по-видимому, не могу сравнивать сразу два варианта. Мне нужно поведение, как если бы я сравнивал id в Objective-C, то есть:

  • true, если оба параметра nil
  • true, если оба имеют значение, а значения также равны
  • false иначе

Есть ли элегантный способ записать это в Swift (в идеале без написания пользовательского расширения)?

Это лучшее, что я придумал:

if !(oldValue != nil && newValue != nil && oldValue == newValue)

Не очень красиво.: (

Ответ 1

Вы можете использовать !==

От Язык быстрого языка

Swift также предоставляет два идентификационных оператора (=== и !==), которые вы используете для проверки ссылок на два объекта, которые ссылаются на один и тот же экземпляр объекта.

Некоторые хорошие примеры и объяснения также находятся в Разница между == и ===

В точке @PEEJWEEJ выполнение следующего результата приведет к false

var newValue: AnyObject? = "String"
var oldValue: AnyObject? = "String"

if newValue === oldValue {
   print("true")
} else {
   print("false")
}

Ответ 2

Предполагая, что вы используете объекты Comparable, это будет работать на что угодно:

func optionalsAreEqual<T: Comparable>(firstVal: T?, secondVal: T?) -> Bool{

    if let firstVal = firstVal, secondVal = secondVal {
        return firstVal == secondVal
    }
    else{
        return firstVal == nil && secondVal == nil
   }
}

Это не совсем короткое и сладкое, но оно выразительное, ясное и многоразовое.

Ответ 3

Мне понравилось решение @Keith. Но я думаю, что это не написано в Swift 4, так как я не могу скомпилировать его с помощью компилятора Swift 4.

Поэтому я конвертировал его код в версию Swift 4 здесь.

Помните, что если вы используете более высокую версию языка Swift, чем Swift 4.1, тогда этот ответ не нужен, поскольку он предоставляет эту функцию по умолчанию. Вы можете обратиться сюда для более подробной информации.

Swift 4 версия кода @Keith:

infix operator ==? : ComparisonPrecedence

func ==? <T: Comparable>(lhs: T?, rhs: T?) -> Bool {
    if let lhs = lhs, let rhs = rhs {
        return lhs == rhs
    } else {
        return lhs == nil && rhs == nil
    }
}

func ==? <T: AnyObject>(lhs: T?, rhs: T?) -> Bool {
    if let lhs = lhs, let rhs = rhs {
        return lhs === rhs
    } else {
        return lhs == nil && rhs == nil
    }
}

Ответ 4

Я определяю пользовательский инфиксный оператор с функцией как для ссылочных типов, так и типов значений.

func ==? <T: Comparable>(lhs: T?, rhs: T?) -> Bool {
    if let lhs = lhs, rhs = rhs {
        return lhs == rhs
    } else{ return lhs == nil && rhs == nil }
}
func ==? <T: AnyObject>(lhs: T?, rhs: T?) -> Bool {
    if let lhs = lhs, rhs = rhs {
        return lhs === rhs
    } else{ return lhs == nil && rhs == nil }
}
infix operator ==? { associativity left precedence 130 }

var aString: String? = nil
var bString: String? = nil
print(aString ==? bString) // true

aString = "test"
bString = "test"
print(aString ==? bString) // true

aString = "test2"
bString = "test"
print(aString ==? bString) // false

aString = "test"
bString = nil
print(aString ==? bString) // false

class TT {}
let x = TT()

var aClass: TT? = TT()
var bClass: TT? = TT()
print(aClass ==? bClass) // false

aClass = TT()
bClass = nil
print(aClass ==? bClass) // false

aClass = nil
bClass = nil
print(aClass ==? bClass) // true

aClass = x
bClass = x
print(aClass ==? bClass) // true

Ответ 5

Вы можете перегрузить оператор == для некоторого Comparable типа

public func ==<T: SomeType>(lhs: T?, rhs: T?) -> Bool {
    switch (lhs,rhs) {
    case (.some(let lhs), .some(let rhs)):
        return lhs == rhs
    case (.none, .none):
        return true
    default:
        return false
    }
}

Или используйте сравнение === для AnyObject, хотя лично я предпочел бы не использовать AnyObject.