Ответ 1
В Swift 3.1 вы также можете добавить расширение к необязательным значениям:
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}
Вы можете создать расширение строки так:
extension String {
func someFunc -> Bool { ... }
}
но что, если вы хотите, чтобы оно применимо к необязательной строке?
var optionalString :String? = ""
optionalString!.someFunc() /* String? does not have a member someFunc */
В Swift 3.1 вы также можете добавить расширение к необязательным значениям:
extension Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}
Вы можете сделать это следующим образом:
protocol OptionalType { typealias A; var opt: A? { get } }
extension Optional: OptionalType { var opt: A? { return self } }
protocol StringType { var get: String { get } }
extension String: StringType { var get: String { return self } }
extension Optional where Wrapped: StringType {
func getOrElse(s: String) -> String {
return self.opt?.get ?? s
}
}
и
let optStr: String? = nil
optStr.getOrElse("hello world")
Причина, по которой вы не можете ограничить Optional
или String
, поскольку это struct
. Сделав псевдопротокол для каждого, теперь мы можем сдерживать, как нам нравится.
Я чувствую, что быстро отбросил множество вещей, чтобы сделать его проще для начинающих, чтобы узнать, или, может быть, язык еще недостаточно созрел.
Optional
, которые возвращают String
С Swift 3 вы не можете напрямую ограничивать метод расширения необязательным String
. Вы можете получить эквивалентный результат с протоколами, как объясняет ответ Даниэля Шин.
Однако вы можете создать метод расширения для необязательного типа любого типа, и я нашел несколько полезных методов, которые имеют возвращаемое значение String
. Эти расширения полезны для записи значений на консоль. Я использовал asStringOrEmpty() на String
необязательный, когда я хочу заменить возможное ноль пустой строкой.
extension Optional {
func asStringOrEmpty() -> String {
switch self {
case .some(let value):
return String(describing: value)
case _:
return ""
}
}
func asStringOrNilText() -> String {
switch self {
case .some(let value):
return String(describing: value)
case _:
return "(nil)"
}
}
}
Пример использования:
var booleanValue: Bool?
var stringValue: String?
var intValue: Int?
print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")
booleanValue = true
stringValue = "text!"
intValue = 41
print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")
Консольный выход:
booleanValue: (nil)
stringValue: (nil)
intValue: (nil)
booleanValue: true
stringValue: text!
intValue: 41
Optional
отличается от указателя nilЭти расширения иллюстрируют, что Optional
отличается от указателя nil. Optional
- это enum
указанного типа (Wrapped
), который указывает, что он имеет или не содержит значения. Вы можете написать расширение в контейнере Optional
, даже если оно не может содержать значения.
Выдержка из быстрой декларации Swift
enum Optional<Wrapped> : ExpressibleByNilLiteral {
/// The absence of a value.
case none
/// The presence of a value, stored as `Wrapped`.
case some(Wrapped)
...
}
В коде отсутствие значения обычно записывается с использованием литерала nil
, а не явного события перечисления .none
.
Обновление:. Обходной путь, который работает с Swift 2 и выше, см. Даниэль Шинс.
Необязательный String сам по себе не является типом, и поэтому вы не можете создать расширение для необязательного типа. В Swift a Optional
представляет собой просто перечисление (плюс немного синтаксического сахара), которое может быть либо None
, либо Some
, которое обертывает значение. Чтобы использовать метод String, вам нужно развернуть свой optionalString
. Для этого вы можете легко использовать дополнительную цепочку:
optionalString?.someFunc()
Если optionalString
не nil
, на него будет вызываться someFunc
. Альтернативный (менее краткий) способ сделать это - использовать необязательную привязку, чтобы установить, имеет ли значение optionalString
значение, прежде чем пытаться вызвать метод:
if let string = optionalString {
string.someFunc() // `string` is now of type `String` (not `String?`)
}
В вашем примере из приведенных ниже комментариев вам не нужно вставлять несколько операторов if
, вы можете проверить, является ли необязательная строка пустой строкой в одном if
:
if optionalString?.isEmpty == true {
doSomething()
}
Это работает, потому что выражение optionalString?.isEmpty
возвращает необязательный Bool (т.е. true
, false
или nil
). Поэтому doSomething()
вызывается только в том случае, если optionalString
не nil
, и если эта строка пуста.
Другой вариант:
if let string = optionalString where string.isEmpty {
doSomethingWithEmptyString(string)
}
extension Optional where Wrapped == String {
var isNil: Bool {
return self == nil
}
Приведенный выше ответ (написанный @Vlad Hatko) работает нормально, но в swift 4 есть некоторые проблемы, поэтому я изменил его на этот.
нашел трюк swift 3
class A{
var name:String!;
init(_ name:String?){
self.name = name;
}
}
extension Optional where Wrapped == String {
func compareText(_ other:String?)->Bool{
switch (self,other){
case let(a?,b?):
return a < b;
case (nil,_):
return true;
default:
return false;
}
}
}
let words:[A] = [A("a"),A(nil),A("b"),A("c"),A(nil)];
// let sorted = words.sorted{ 0.name.compareText($1.name) }
// trick
let sorted = words.sorted{ ($0.name as String?).compareText($1.name) }
print(sorted.map{$0.name});
Начиная с Xcode 9.3, вы можете использовать эту небольшую модификацию ответа @Vladyslav:
extension Optional where Wrapped == String {
var isEmpty: Bool {
return self?.isEmpty ?? true
}
}
В Swift 4.1 я получал Optional is ambiguous for type lookup in this context
ошибке сборки Optional is ambiguous for type lookup in this context
. Чтобы исправить, вы должны явно добавить пространство имен Swift к типу:
extension Swift.Optional where Wrapped == String {
var isBlank: Bool {
return self?.isBlank ?? true
}
}