Wie füge ich eine optionale Zeichenfolgenerweiterung hinzu?


73

Sie können eine String-Erweiterung wie folgt erstellen:

extension String {
   func someFunc -> Bool { ... }
}

aber was ist, wenn Sie möchten, dass es auf optionale Zeichenfolgen angewendet wird?

var optionalString :String? = ""
optionalString!.someFunc() /* String? does not have a member someFunc */

Sie müssen es immer auspacken, um eine Erweiterung anzuwenden
Leo Dabus

Also habe ich es ausgepackt, aber vielleicht habe ich das falsch gemacht? optionalString!.someFunc()
Vol7ron

Wenn Sie sicher sind, dass optional nicht null
zurückgibt, ist das

Es wird manchmal Null sein, deshalb wird optional verwendet. Ich debugge immer noch, ich vermute, mein Spielplatz ist nicht synchron. Ich kann diese Frage gleich schließen.
Vol7ron

@LeonardoSavioDabus Ja, das hatte ich gehofft, in die Erweiterung zu setzen und die Erweiterung zum Lösen zu verwenden. Ich mag die verschachtelten IF-Anweisungen nicht. Ich vermute, dass sich eine zukünftige Version von Swift irgendwann verbessern wird
Vol7ron

Antworten:


184

In Swift 3.1 können Sie optionalen Werten auch eine Erweiterung hinzufügen:

extension Optional where Wrapped == String {
  var isBlank: Bool {
    return self?.isBlank ?? true
  }
}

Wie sieht seine Verwendung aus?
Shim

1
if myOptionalString?.isBlank {gibt den Wert des optionalen Typs Bool an? noch nicht ausgepackt.
Shim

Und auch einen Type Wrapped constrained to non-protocol type StringFehler bekommen . Hier ist eine wahrscheinlich relevante Diskussion über Reddit, die möglicherweise in Zukunft behoben wird.
Shim

Ist das möglich where Wrapped == T?
Declan McKenna

3
Verwendung ist myOptionalString.isBlank. Ohne das, ?da Sie es auf dem optionalen selbst anstatt auf dem aufrufen String.
Lammert

12

Sie können es so machen:

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
  }
}

Und:

let optStr: String? = nil
optStr.getOrElse("hello world")

Der Grund, den Sie nicht einschränken können Optionaloder Stringfür diese Angelegenheit, ist, weil sie sind struct. Indem wir für jedes Pseudoprotokoll erstellen, können wir jetzt nach Belieben einschränken.

Ich habe das Gefühl, dass Swift viele Dinge aufgegeben hat, um Anfängern das Lernen zu erleichtern , oder vielleicht ist die Sprache noch nicht genug gereift.


Ist das neu Ich dachte, ich hätte in der Vergangenheit versucht, den optionalen Typ zu erweitern
Vol7ron

@ vol7ron Dies ist jetzt mit der Protokollerweiterung möglich, die mit Swift 2 geliefert wurde.
Daniel Shin

8

Erweiterungen dazu Optionalgeben a zurückString

Ab Swift 3 können Sie eine Erweiterungsmethode nicht direkt auf eine optionale beschränken String. Sie können das gleiche Ergebnis mit Protokollen erzielen, wie die Antwort von Daniel Shin erklärt.

Sie können jedoch eine Erweiterungsmethode für eine Option eines beliebigen Typs erstellen, und ich habe einige nützliche Methoden gefunden, die einen StringRückgabewert haben. Diese Erweiterungen sind hilfreich, um Werte in der Konsole zu protokollieren. Ich habe asStringOrEmpty () für eine StringOption verwendet, wenn ich eine mögliche Null durch eine leere Zeichenfolge ersetzen möchte.

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)"
        }
    }
}

Beispiel Verwendung:

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())")

Konsolenausgabe:

booleanValue: (nil)
stringValue: (nil)
intValue: (nil)

booleanValue: true
stringValue: text!
intValue: 41

 

Optional anders als Null Zeiger

Diese Erweiterungen veranschaulichen, dass an Optionalsich von einem Nullzeiger unterscheidet. An Optionalist ein enumvom angegebenen Typ ( Wrapped), der angibt, dass es einen Wert enthält oder nicht. Sie können eine Erweiterung in den Optional"Container" schreiben , obwohl dieser möglicherweise keinen Wert enthält.

Auszug aus der optionalen Swift-Erklärung

enum Optional<Wrapped> : ExpressibleByNilLiteral {

    /// The absence of a value.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)

    ...
}

Im Code wird das Fehlen eines Werts normalerweise nileher mit dem Literal als mit dem expliziten .noneAufzählungsfall geschrieben.


5

In Swift 4.1 wurde ein Optional is ambiguous for type lookup in this contextBuildfehler angezeigt. Um dies zu beheben, müssen Sie den Swift-Namespace explizit zum Typ hinzufügen:

extension Swift.Optional where Wrapped == String {
    var isBlank: Bool {
        return self?.isBlank ?? true
    }
}

4
extension Optional where Wrapped == String {
var isNil: Bool {
    return self == nil
}

Die obige Antwort (geschrieben von @Vlad Hatko) funktioniert gut, aber in Swift 4 gibt es einige Probleme, deshalb habe ich sie geändert.


3

Seit Xcode 9.3 können Sie diese geringfügige Änderung der Antwort von @ Vladyslav verwenden:

extension Optional where Wrapped == String {

    var isEmpty: Bool {
        return self?.isEmpty ?? true
    }

}

2

Update: Eine Problemumgehung, die mit Swift 2 und höher funktioniert, finden Sie in der Antwort von Daniel Shin


Ein optionaler String ist an und für sich kein Typ, daher können Sie keine Erweiterung für einen optionalen Typ erstellen. In Swift Optionalist an nur eine Aufzählung (plus ein bisschen syntaktischer Zucker), die entweder sein Nonekann oder Someeinen Wert umschließt. Um Ihre String-Methode zu verwenden, müssen Sie Ihre auspacken optionalString. Sie können einfach eine optionale Verkettung verwenden, um dies zu erreichen:

optionalString?.someFunc()

Wenn optionalStringnicht nil, someFuncwird darauf aufgerufen. Eine alternative (weniger prägnante) Möglichkeit besteht darin, mithilfe der optionalen Bindung festzustellen, ob optionalStringein Wert vorhanden ist oder nicht , bevor Sie versuchen, die Methode aufzurufen:

if let string = optionalString {
    string.someFunc()    // `string` is now of type `String` (not `String?`)
}

In Ihrem Beispiel aus den Kommentaren unten müssen Sie nicht mehrere ifAnweisungen verschachteln . Sie können überprüfen, ob die optionale Zeichenfolge eine leere Zeichenfolge in einer einzelnen ist if:

if optionalString?.isEmpty == true {
    doSomething()
}

Dies funktioniert , weil der Ausdruck optionalString?.isEmptyeines optionalen Bool (dh zurückgibt true, falseoder nil). So doSomething()wird nur aufgerufen werden , wenn optionalStringist nicht nil , und wenn die Zeichenfolge leer ist .

Eine andere Alternative wäre:

if let string = optionalString where string.isEmpty {
    doSomethingWithEmptyString(string)
}

Ich habe versucht, eine Erweiterung zu erstellen, um eine optionale Bindung zu vermeiden. Es scheint, dass das nicht möglich sein wird
vol7ron

@ vol7ron Kann ich fragen warum? Ist die optionale Verkettung / Bindung irgendwie unpraktisch?
Stuart

Stellen Sie sich eine optionale Zeichenfolge vor, die Sie überprüfen möchten, ob sie ein Zeichen enthält. Meines Wissens müssen Sie verschachtelte verwenden, wenn if let str=optString { if str.isEmpty { doSomething(); } }ich lieber eine isBlankErweiterung einer optionalen Zeichenfolge sein
möchte

Mein Beispiel (im Kommentar) war nicht vollständig, aber isBlank ist im Allgemeinen null oder isEmpty führt zu true.
Vol7ron

Richtig, Sie finden Wege, die bei weitem nicht so lesbar sind wie in anderen Sprachen. if optStr.isBlank { … }Boom. Für etwas so Neues wie Swift würde man denken, dass das Endziel darin besteht, lesbar zu sein, da lesbar wartbar ist.
Vol7ron

1

fand einen Trick schnell 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});

1

Sie können eine optionale Zeichenfolgenerweiterung erstellen. Ich habe Folgendes getan, um eine optionale Zeichenfolge auf leer zu setzen, wenn sie null war:

extension Optional where Wrapped == String {

    mutating func setToEmptyIfNil() {
        guard self != nil else {
            self = ""
            return
        }
    }

}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.