Um zwei Swift-Strukturwerte zusammenzuführen, schreiben Sie eine Funktion, die für jedes Feld die Quelle auswählt und einen neuen Wert zurückgibt. Dieses macOS-Beispiel kombiniert zwei synthetische Zeichenflächenstile mit expliziten Regeln für Ersetzen, Beibehalten, optionales Leeren und ungültige Eingaben.

Vor dem Schreiben der Zusammenführung festlegen, welcher Wert Vorrang hat

Die Aufgabe für Leser besteht darin, auf einem Mac aus zwei vorhandenen Werten einen Zeichenflächenstil zu berechnen. Nennen Sie die Eingaben base und incoming: Diese Namen beschreiben ihre Rollen, während ein Vertrag auf Feldebene festlegt, welcher Wert Vorrang hat. Die Berechnung verwendet ausschließlich synthetische Werte im Arbeitsspeicher.

Swift beschreibt Strukturen als Werttypen und unterstützt das Erstellen von Instanzen über Initializer. Apples dokumentierte API merge(_:uniquingKeysWith:) gehört zu Dictionary; sie löst doppelte Schlüssel mithilfe eines Closures auf. Definieren Sie für diese benutzerdefinierte Struktur eine Zusammenführungsfunktion, deren Implementierung die beabsichtigten Feldregeln ausdrückt. Strukturen und Klassen, Dictionary merge(_:uniquingKeysWith:).

Verwenden Sie den folgenden beispielhaften Vertrag. Dabei handelt es sich um Designentscheidungen für dieses Beispiel und nicht um ein von Swift automatisch bereitgestelltes Verhalten.

Feld Regel Beispielhafte Folge
title Den eingehenden Text übernehmen, vorangestellte und nachgestellte Leerzeichen sowie Zeilenumbrüche entfernen und anschließend ein leeres Ergebnis ablehnen " Tile Study \n" wird zu \"Tile Study\"
spacing Den Basiswert beibehalten; er muss nichtnegativ sein Der Basiswert 12 hat Vorrang vor dem eingehenden Wert 30
showsGrid Den eingehenden booleschen Wert unverändert übernehmen Der eingehende Wert false ersetzt den Basiswert true
caption Den eingehenden Optional-Wert unverändert übernehmen Der eingehende Wert nil entfernt die Basisbeschriftung

Diese Tabelle unterscheidet drei leicht zu verwechselnde Vorgänge: eine Quelle auswählen, den ausgewählten Wert normalisieren und das Ergebnis validieren. Die eingehende Abstandsangabe wird absichtlich nicht verwendet. Sie kann die Ausgabe nicht ändern, selbst wenn ihr Wert von der Basisabstandsangabe abweicht.

Foundation stellt mit trimmingCharacters(in:) einen neuen String bereit, bei dem übereinstimmende Zeichen an beiden Enden entfernt wurden. Apple dokumentiert die Verwendung von .whitespacesAndNewlines, um Leerraum am Anfang und Ende zu entfernen; ein String, der ausschließlich aus diesen Zeichen besteht, wird leer. Die Ablehnung dieses leeren Ergebnisses ist unsere zusätzliche Regel. Foundation trimmingCharacters(in:).

Ein vollständiges lokales Beispiel auf dem Mac ausführen

Verwenden Sie für dieses Beispiel eine Swift-6-Toolchain. Swift.org stellt macOS-Installationsanweisungen über Swiftly bereit und nennt Xcode als Option für die Entwicklung auf Apple-Plattformen. Öffnen Sie Terminal und führen Sie swift --version aus, um zu bestätigen, dass der Befehl verfügbar ist. Falls er fehlt, befolgen Sie vor dem Fortfahren die Installationsanweisungen. Swift unter macOS installieren, Erste Schritte.

Speichern Sie das folgende vollständige Programm in einem Arbeitsordner als MergeStyles.swift. Es importiert Foundation, definiert beide Eingabewerte, führt die Zusammenführung aus, prüft das erwartete Ergebnis und testet die beiden Fälle, in denen Werte abgelehnt werden. Die Berechnung stellt keine Netzwerkanfragen und speichert keine Anwendungsdaten.

import Foundation

struct PanelStyle: Equatable, Sendable {
    let title: String
    let spacing: Int
    let showsGrid: Bool
    let caption: String?
}

enum StyleMergeError: Error {
    case emptyTitle
    case negativeSpacing
}

func mergeStyles(
    base: PanelStyle,
    incoming: PanelStyle
) throws -> PanelStyle {
    let title = incoming.title.trimmingCharacters(
        in: .whitespacesAndNewlines
    )
    guard !title.isEmpty else {
        throw StyleMergeError.emptyTitle
    }
    guard base.spacing >= 0 else {
        throw StyleMergeError.negativeSpacing
    }

    return PanelStyle(
        title: title,
        spacing: base.spacing,
        showsGrid: incoming.showsGrid,
        caption: incoming.caption
    )
}

let base = PanelStyle(
    title: "Shape Study",
    spacing: 12,
    showsGrid: true,
    caption: "Blue squares"
)
let incoming = PanelStyle(
    title: "  Tile Study \n",
    spacing: 30,
    showsGrid: false,
    caption: nil
)

let expected = PanelStyle(
    title: "Tile Study",
    spacing: 12,
    showsGrid: false,
    caption: nil
)

let result = try mergeStyles(base: base, incoming: incoming)
precondition(result == expected)
precondition(base.title == "Shape Study")
precondition(base.caption == "Blue squares")
precondition(incoming.title == "  Tile Study \n")

print(result.title)
print("spacing: \(result.spacing)")
print("grid: \(result.showsGrid)")
print("caption cleared: \(result.caption == nil)")

let blankTitle = PanelStyle(
    title: " \n\t",
    spacing: 30,
    showsGrid: false,
    caption: nil
)
do {
    _ = try mergeStyles(base: base, incoming: blankTitle)
    preconditionFailure("Expected emptyTitle")
} catch StyleMergeError.emptyTitle {
    print("Rejected blank title")
}

let negativeBase = PanelStyle(
    title: "Shape Study",
    spacing: -1,
    showsGrid: true,
    caption: "Blue squares"
)
do {
    _ = try mergeStyles(base: negativeBase, incoming: incoming)
    preconditionFailure("Expected negativeSpacing")
} catch StyleMergeError.negativeSpacing {
    print("Rejected negative spacing")
}

Führen Sie im Ordner, der die Datei enthält, Folgendes aus:

swift MergeStyles.swift

Die erwartete Ausgabe ist unten dargestellt. Swift war in der Rechercheumgebung nicht verfügbar, daher wurde das Programm dort geprüft, aber nicht ausgeführt.

Tile Study
spacing: 12
grid: false
caption cleared: true
Rejected blank title
Rejected negative spacing

Der Initializer ist die zentrale Entscheidungshilfe: Jedes Argument bezeichnet entweder das ausgewählte Eingabefeld oder einen benannten Zwischenwert. Keine der beiden Eingaben wird verändert. Die Wertsemantik von Swift unterstützt diesen Ansatz; die Felder hier sind selbst Werttypen. Wenn Sie später ein Feld mit einem Klassenwert einführen, wird die referenzierte Klasseninstanz beim Kopieren der umgebenden Struktur nicht unabhängig kopiert. Strukturen und Klassen.

Die Validierung erfolgt vor dem Erstellen der Instanz. Ein leerer eingehender Titel löst einen Fehler aus, statt stillschweigend den Basistitel wiederherzustellen, und eine negative Basisabstandsangabe löst einen Fehler aus, statt stillschweigend auf null begrenzt zu werden. Wenn beide Bedingungen nicht erfüllt sind, wird zuerst emptyTitle gemeldet, weil diese Guard-Anweisung zuerst ausgeführt wird. Aufrufer sollten den Fehler behandeln, bevor sie ein Ergebnis verwenden.

Entscheiden, ob ein optionaler Wert „leeren“ oder „beibehalten“ bedeutet

Im Hauptbeispiel gilt incoming.caption als vollständige gewünschte Beschriftung. nil bedeutet daher: „Das Ergebnis hat keine Beschriftung.“ Dadurch wird das Leeren im Vertrag ausdrücklich festgelegt, auch wenn die Darstellung lediglich ein Optional-Wert ist.

Eine andere Anforderung könnte besagen, dass nil bedeutet: „Die Basisbeschriftung beibehalten.“ Für diese Richtlinie wäre das ersetzende Initializer-Argument caption: incoming.caption ?? base.caption. Swifts Nil-Coalescing-Operator wählt den linken Operanden aus, wenn dieser einen Wert enthält, und verwendet andernfalls den rechten Operanden. Dies ist ein benanntes Ersatzfragment und kein zweites vollständiges Programm. Nil-Coalescing-Operator.

Mit dieser Fallback-Richtlinie lässt sich derselbe eingehende Wert nil nicht zugleich zum Leeren verwenden. Legen Sie die Bedeutung fest, bevor Sie den Ausdruck übernehmen. Ein leerer String ist ein weiterer, davon unabhängiger Wert: Bei beiden Richtlinien bleibt eine eingehende Beschriftung \"\" eine leere Beschriftung, sofern Sie nicht ausdrücklich eine Normalisierung hinzufügen.

Wenn eine künftige Bearbeitungsoperation alle drei Aktionen benötigt – beibehalten, ersetzen und leeren –, stellen Sie die Aktion separat dar. Das folgende unabhängige, vollständige Programm veranschaulicht eine Operation nur für Beschriftungen mithilfe eines benutzerdefinierten Enums:

enum CaptionChange {
    case keep
    case set(String?)
}

func applying(
    _ change: CaptionChange,
    to caption: String?
) -> String? {
    switch change {
    case .keep:
        return caption
    case .set(let replacement):
        return replacement
    }
}

let original: String? = "Blue squares"
precondition(applying(.keep, to: original) == original)
precondition(applying(.set("Green circles"), to: original)
    == "Green circles")
precondition(applying(.set(nil), to: original) == nil)
print("Caption actions checked")

Dieses Enum ist eine beispielhafte Erweiterung des Vertrags. Verwenden Sie die ursprüngliche Funktion mit zwei Werten, wenn beide Eingaben vollständige Stile sind; führen Sie einen Aktionstyp ein, wenn die Eingabe tatsächlich Änderungen beschreibt.

Die Priorität mit absichtlich unterschiedlichen Werten prüfen

Bei einer aussagekräftigen Prüfung der Zusammenführung unterscheiden sich die konkurrierenden Felder. Wenn beide Eingaben die Abstandsangabe 12 verwenden und beide das Raster aktivieren, bleibt eine falsche Quellenauswahl möglicherweise unbemerkt. Das Beispiel verwendet stattdessen die Abstandsangaben 12 und 30 sowie true und false, sodass das erwartete Ergebnis diese Entscheidungen sichtbar macht.

Prüfen Sie vor der Anpassung der Funktion anhand des beabsichtigten Vertrags die folgenden Grenzfälle:

  • false muss false bleiben; verwenden Sie kein boolesches ODER, um Ersetzen auszudrücken.
  • Die eingehende Beschriftung nil muss gemäß der Hauptrichtlinie die Beschriftung leeren.
  • Leerraum am Anfang und Ende des Titels muss entfernt werden, interne Leerzeichen müssen erhalten bleiben.
  • Ein eingehender Titel, der nur aus Leerraum besteht, muss einen Fehler auslösen, ohne auf die Basis zurückzufallen.
  • Die Basisabstandsangabe 0 muss akzeptiert werden; eine negative Basisabstandsangabe muss einen Fehler auslösen.
  • Beim Vertauschen der Argumente müssen die Regeln auf die neu zugewiesenen Rollen angewendet werden.

Der letzte Punkt ist wichtig, weil diese Zusammenführung von der Reihenfolge abhängt. Bei umgekehrter Reihenfolge der Beispieldaten werden \"Shape Study\", die Abstandsangabe 30, die Rastereinstellung true und die Beschriftung \"Blue squares\" ausgewählt. Das ist das Ergebnis der festgelegten Regeln. Benennen Sie beide Argumente an den Aufrufstellen, damit ihre Rollen sichtbar bleiben.

Wenn Sie ein Feld hinzufügen, erweitern Sie die Tabelle, den Initializer und eine Prüfung mit konkurrierenden Werten. Wenn ein künftiger verschachtelter Stil mehrere unabhängige Einstellungen enthält, entscheiden Sie, ob der gesamte verschachtelte Wert ersetzt wird oder ob jedes seiner Felder eine eigene Priorität hat. Die aktuelle Implementierung definiert nur die vier gezeigten Felder.

Nebenläufigkeit von der Zusammenführungsrichtlinie getrennt halten

Diese Berechnung ist synchron: Sie liest ihre Argumente, validiert ausgewählte Werte und gibt ein Ergebnis zurück oder löst einen Fehler aus. Um die Priorität festzulegen, benötigt sie weder einen Task noch einen Actor. Swift beschreibt asynchrone Funktionen als Funktionen, die angehalten werden können; das Hinzufügen asynchroner Ausführung legt nicht fest, welches Feld Vorrang hat. Nebenläufigkeit.

Das Beispiel deklariert PanelStyle als Sendable, um auszudrücken, dass seine Werte über Nebenläufigkeitsgrenzen hinweg übergeben werden können. Apple verlangt, dass eine sendbare Struktur sendbare Member enthält. Diese Konformität betrifft die Übertragung von Werten; die Feldauswahlregeln ergeben sich weiterhin ausschließlich aus mergeStyles. Sendable.

Halten Sie für diese lokale Zeichenflächenaufgabe den Vertrag neben der Funktion fest und verwenden Sie deutlich unterschiedliche Beispieldaten. Das resultierende Objekt sollte sich Feld für Feld erklären lassen: ein normalisierter eingehender Titel, eine beibehaltene Basisabstandsangabe, die eingehende Rastereinstellung und eine eingehende Beschriftung, mit der sich der vorherige Wert ausdrücklich leeren lässt.

Quellen