Swift 5.2. Übersicht aller Änderungen

Ende März wurde Swift 5.2 für Xcode 11.4 veröffentlicht. Es hat die Fehlerdiagnose, die Abhängigkeitsanalyse und die SwiftPM-Funktionalität verbessert. Eine Übersicht über einige Änderungen wurde bereits auf Habré veröffentlicht. Im selben Artikel wird die Entwicklung der Sprache selbst mit möglichen Anwendungsbeispielen betrachtet.


 

SE-0249 KeyPath als Funktion


Die Verknüpfung fügt keyPath in eine Funktion ein, bei der der Eingabeparameter das Objekt selbst und das Ergebnis seine Eigenschaft ist.

Schauen wir uns ein Beispiel an. Erstellen Sie eine Reihe einfacher Modelle:

struct Model {
    let isHidden: Bool
}
 
let modelArray = [Model(isHidden: true), Model(isHidden: false)]

Filtern Sie das Array nach der Eigenschaft isHidden. Unten sind 3 Beispiele mit dem gleichen Ergebnis:

// 1
let funcIsHidden: (Model) -> Bool = \.isHidden
modelArray.filter(funcIsHidden)
 
// 2
modelArray.filter(\.isHidden as (Model) -> Bool)
 
// 3
modelArray.filter(\.isHidden)

Die deklarierte Funktionalität funktioniert im folgenden Beispiel nicht:

//   
let selfFunc: (Model) -> Model = \.self
modelArray.compactMap(selfFunc)
 
// ERROR: Expression type '(Model) -> Model' is ambiguous without more context
modelArray.compactMap(\.self as (Model) -> Model)
 
// ERROR: Key path value type 'Optional<_>' cannot be converted to contextual type '_'
modelArray.compactMap(\.self)

Im Gegensatz zu keyPath funktioniert die automatische Vervollständigung nicht.

Es ist praktisch, um mit Arrays in Funktionen wie Filtern, Zuordnen, Reduzieren, Sortieren und dergleichen zu arbeiten.

SR-6118- Indizes können Standardeinstellungen enthalten


Alle Funktionsparameter können auf den Standardwert gesetzt werden. Erstellen Sie eine Box-Struktur, die ein Array von Elementen und eine Indexfunktion enthält, um darauf zuzugreifen.

struct Box {
    let items: [String]
    
    subscript(_ index: Int = 0) -> String {
        items[index]
    }
}

Um nun auf das erste Element zuzugreifen, können Sie den Index weglassen:

let box = Box(items: ["laptop, , mobile phone"])
let firstItem = box[] // "laptop"

SR-2189 Lokale Funktionen unterstützen Standardparameter aus der externen Sichtbarkeit


In der täglichen Praxis werden lokale Funktionen selten verwendet. Es kann schwierig sein, ihre Verwendung zu rechtfertigen. Trotzdem gebe ich zu, dass dies in bestimmten Fällen nützlich sein kann.

Als Beispiel erstellen wir eine Funktion, in der wir eine lokale beschreiben:

func getXPosition() {
    func calculateWidth(x: CGFloat = minX) -> CGFloat { ... }
    let minX: CGFloat = 0
    ...
}

Als Standardparameter für die Funktion berechneWeite können wir die Werte innerhalb der Funktion getXPosition verwenden.

SE-0253 Verwenden von Werten als Funktionen


Mit der Funktionalität, ähnlich wie bei @dynamicCallable, können Sie einen Wert als Funktion verwenden. Aber es ist eine Implementierung des "statischen Aufrufs".

In der Praxis ist alles sehr einfach: Um Funktionen aufzurufen, können Sie Werte als Methoden bezeichnen.

Erstellen Sie eine Player-Struktur:

struct Player {
    private(set) var isRunning = false
    
    mutating func callAsFunction() {
        isRunning.toggle()
    }
}

Erstellen Sie eine Instanz von Player und bezeichnen Sie sie als Funktion:

var player = Player()
print(player.isRunning) // false
player()
print(player.isRunning) // true

Gleichzeitig ist es verboten, ein Objekt als Funktion zu gießen und daher zu übergeben:

// ERROR: Cannot convert value of type 'Player' to type '() -> Void' in coercion
let playerAsFunc = player as () -> Void

Sie können einer Klasse, Struktur oder einem Protokoll so viele Methoden wie callAsFunction hinzufügen:

extension Player {
    func callAsFunction(isRunning: Bool = false) throws { ... }
    func callAsFunction() -> Bool { ... }
}

Die Anwendung ist in Werten möglich, die mathematische Funktionen, komplexe Ausdrücke oder in Fällen sind, in denen der Wert eine dominante Funktion hat. Trotzdem sollten Sie diese Funktionalität nicht missbrauchen, da sie irreführend sein kann.

SR-4206 Fehler behoben, bei dem die Funktion mit generischen Parametern überschrieben wurde


Wenn Sie die Funktion überschreiben, können Sie den generischen Typ nicht mehr ändern oder einschränken. Erstellen Sie beispielsweise eine CarBuilder-Klasse und erben Sie sie, indem Sie die add-Methode überschreiben:

protocol PowerfullEngine { }
 
class CarBuilder {
  func add<T>(engine: T) {}
}
 
class MercedesBuilder: CarBuilder {
    //  
    override func add<T>(engine: T) {}
    // ERROR: Overridden method 'add' has generic signature <T where T : PowerfullEngine> which is incompatible with base method's generic signature <T>; expected generic signature to be <T>
    override func add<T: PowerfullEngine>(engine: T) {}
}

SR-11298 Das Erweitern eines Protokolls ohne Klasseneinschränkungen erbt diese Einschränkung


Im Falle von Selbstbeschränkungen gilt für die Erweiterung die Beschränkung.

Erstellen Sie beispielsweise das Menü- und Erweiterungsprotokoll mit einer Klasseneinschränkung:

protocol Menu {}
 
class SideMenu: Menu {
  var sectionHeight = 0
}
 
extension Menu where Self: SideMenu {
  var menuHeight: Int {
    get { sectionHeight * 20 }
    set { sectionHeight = newValue / 20 }
  }
}

Gleichzeitig mutiert der Setter nicht, als ob das Protokoll eine Klassenbeschränkung hätte.

SR-11429 Casting für Funktionen mit Beschriftungen


Beim Casting werden Funktionen zum Schreiben von Etiketten jetzt entfernt. Dank dessen können Sie Funktionen mit Beschriftungen umwandeln. Bisher funktionierte diese Funktionalität nur in Funktionen ohne Beschriftung:

func setX(x: Int) {}
(setX as (Int) -> Void)(5)

Die Standardwerte werden jedoch nicht gespeichert:

func setPoint(x: Int, y: Int = 0) {}
(setPoint as (Int, Int) -> Void)(5, 1)

Dies gilt auch für Generika, sodass dieser Code nicht mehr gültig ist.

typealias Point<T> = T
func set(x: Int) {}
 
//  
(set as Point)(5) 
 
// ERROR: Extraneous argument label 'x:' in call
(set as Point)(x: 5) 

SR-11841 Die Filterfunktion (_ :) wird in verzögerten Sammlungen in der erwarteten Reihenfolge aufgerufen


Erstellen Sie eine faule Sammlung und rufen Sie die Zählmethode auf:

let lazyArray = [0]
    .lazy
    .filter { _ in print("A"); return true }
    .filter { _ in print("B"); return true }
 
//  A B
_ = lazyArray.count

Vor dem Ergebnis des Anrufs war das Gegenteil: B A.

SR-2790 Eine Reihe von Typinitialisierern der UnsafePointer / UnsafeBufferPointer-Familie geben jetzt eine Warnung aus


Die Einschränkung gilt für Zeichenfolgen, Arrays und Inout-Parameter, die außerhalb des Funktionsaufrufs nicht vorhanden sind.

Erstellen Sie eine Struktur, die UnsafePointer während der Initialisierung als Parameter verwendet:

struct Game {
    let info: UnsafePointer<Int8>
}
 
func createGame() {
    var i: Int8 = 0
    
    // WARNING: Initialization of 'UnsafePointer<Int8>' results in a dangling pointer
    _ = Game(info: .init(&i))
    
    // WARNING: Passing '[Int8]' to parameter, but argument 'character' should be a pointer that outlives the call to 'init(character:)'
    _ = Game(info: [1, 2])
    
    // WARNING: Passing 'String' to parameter, but argument 'character' should be a pointer that outlives the call to 'init(character:)
    _ = Game(info: "")
}

Es gab 9 Änderungen in dieser Version, aber sie führten definitiv neue Funktionen ein. Ich gehe davon aus, dass KeyPath als Funktion am häufigsten verwendet wird.

Und wir freuen uns auf die nächste Version. Anscheinend wird Swift unter Windows unterstützt und es werden interessante Funktionen angezeigt , z. B. die Verwendung von self bei der Flucht vor Schließungen ohne Zugriff über self. * ( SE-0269 ) und die Funktionalität von Generika ( SE-0267 ) werden erweitert .

Quelle

All Articles