SE-0412 дальнейшее укрепление Swift Возможность предотвращения гонок данных во время компиляции.
Если вы пишете код, включающий общее состояние, и не гарантируете, что это общее состояние безопасно при использовании в нескольких потоках, во многих местах вы столкнетесь с проблемами гонки данных.
В Swift 5.10 компилятор позволяет вам получить доступ к общему изменяемому состоянию из параллельного контекста только при следующих обстоятельствах:
Sendable
(существоватьэтотвнутри Понятноразвязать Болеео Sendable
информация)actor
(нравиться @MainActor
или Напишите самииз actor
)var mutableGlobal = 1
// warning: var 'mutableGlobal' is not concurrency-safe because it is non-isolated global shared mutable state
// (unless it is top-level code which implicitly isolates to @MainActor)
final class NonsendableType {
init() {}
}
struct S {
static let immutableNonsendable = NonsendableType()
// warning: static property 'immutableNonsendable' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor
}
В любом другом случае компилятор посчитает одновременный доступ к общему состоянию небезопасным.
Если вы предпримете шаги, чтобы избежать Swift одновременно actor
и Sendability(Например,потому чтодлятысуществоватьиметь дело сиспользовать Семафор или DispatchQueue Чтобы синхронизировать доступ к устаревшему коду, вы можете сделать это, изменив общую Тег переменной ситуации для nonisolated(unsafe)
чтобы отказаться от проверки параллелизма на них. Этот тег сообщит компилятору, что он не требует каких-либо проверок безопасности помеченных свойств. Вы убедились, что код можно безопасно использовать в параллельном контексте;
nonisolated(unsafe) var global: String
Волясвойствоотметкадля nonisolated(unsafe)
Подобно принудительной распаковке файла, вы можете быть уверены, что ваш код безопасен. будет работать как положено Работа,Но ты сам по себе. ты сказал компилятору,Ты знаешь, что ты существуешь,Вам не нужен компилятор для выполнения каких-либо проверок.
Всякий раз, когда вы хотите использовать nonisolated(unsafe)
, вам следует спросить себя, можете ли вы на самом деле изолировать отмеченный вами тип в глобальном actor
,или можете ли вы сделатьсвойствоизтип Sendable
И неизменяемый.
@UIApplicationMain
и @NSApplicationMain
SE-0383 @UIApplicationMain
и @NSApplicationMain
были когда-то iOS и macOS Стандартный способ объявления приложений синтетических точек входа для конкретных платформ. с Swift 5.3 представлять @main
После атрибута эти функции устарели.
Эта версия будет в Swift 6 Раньше Устарело эти атрибуты точки входа заменялись, вместо этого использовали @main
,иисуществовать Swift 6 Их использование приведет к ошибкам.
@UIApplicationMain // warning: '@UIApplicationMain' is deprecated in Swift 5
// fixit: Change `@UIApplicationMain` to `@main`
final class MyApplication: UIResponder, UIApplicationDelegate {
/**/
}
Следует использовать:
@main
final class MyApplication: UIResponder, UIApplicationDelegate {
/**/
}
Допротоколвообще не может быть вложенным,таквсегда должендав модулеиз Вершинатип.SE-0404 Swift 5.10 Это ограничение будет смягчено.
Например,TableView.Delegate
Естественно, это связано с представлением таблицы из протокола делегата. Вы должны объявить это вот так - вложенный в их TableView
В категории:
class TableView {
protocol Delegate: AnyObject {
func tableView(_: TableView, didSelectRowAtIndex: Int)
}
}
class DelegateConformer: TableView.Delegate {
func tableView(_: TableView, didSelectRowAtIndex: Int) {
// ...
}
}
Протокол также может быть вложен в неуниверсальные замыкания функций. Истинный,Это из имеет несколько ограниченную практичность.,Потому что для этих протоколиз вся согласованность должна также существовать одна и та же индивидуальная функция.
SE-0411: Выражения значений по умолчанию теперь могут иметь ту же изоляцию, что и включающая функция или соответствующее сохраненное свойство Isolate:
@MainActor
func requiresMainActor() -> Int { ... }
class C {
@MainActor
var x: Int = requiresMainActor()
}
@MainActor func defaultArg(value: Int = requiresMainActor()) { ... }
Для изолированных значений по умолчанию хранимых свойств неявная инициализация происходит только в телах инициализации с такой же изоляцией. Это закрывает важную уязвимость безопасности в гонке данных, из-за которой значение по умолчанию для глобальной изоляции субъекта может непреднамеренно выполняться синхронно извне субъекта.
Данное предложение в основном решает следующие проблемы:
actor
Изоляция значений по умолчанию может неожиданно actor
Операция внешней синхронизации, нарушение actor
Изоляция из принципа.проходитьпредставлять Изолированное выражение значения по умолчанию,Предложение обеспечивает более безопасный и последовательный способ обработки значений по умолчанию в параллельных средах.,Уменьшает потенциальные ошибки параллелизма,Улучшена читаемость и ремонтопригодность кода.
Более:function parameter isolate
SE-0327 предназначен для укрепления actor
определение, ясно actor
Примеры изданные Когда начинается и заканчивается карантин, и существует ли он actor
из init
и deinit
Объявляет, что можно сделать в основном теле.
actor
или глобальный actor
Изолированный тип (GAIT) из Неделегированного инициализатора необходим для инициализации всех свойств хранения в этом типе.
Хотя actor
да Цитироватьтип,Но они из инициализаторов делегатов будут следовать тем же основным правилам, что и тип значения.,Прямо сейчас:
self.init
из, то это инициализатор делегата. Нет необходимости использовать convenience
Ключевые слова.self
Прежде существование должно быть призвано на всех путях self.init
。actor
и class
Причины такого различия между типизда actor основания не наследуются, поэтому они могут избавиться от сложности делегатов инициализатора класса. ПОХОДКА use использует ту же синтаксическую форму, что и обычный класс, для определения инициализатора делегата.
init
и deinit
Единственная разница между изда, деинит Доступен только Sendable атрибут, в то время как init Может получить доступ к не- Sendable свойство.
Основное содержание включает в себя:
Это индивидуальное предложение из цели принято четко Actor Инициализатор строки для правил, чтобы гарантировать Actor существуют из правильности и безопасности в параллельной среде. Это для Actor Процесс инициализации предоставляет четкие рекомендации, которые помогут разработчикам писать более устойчивые, надежные и параллельные приложения.
пройти спецификацию Actor Инициализатор семантики и ограничений, предложение направлено на улучшение Swift Модель параллелизма, обеспечивающая согласованность и предсказуемость, улучшает использование разработчиками. Actor из Опыта и Эффективности.
if
иswitch
выражениеSE-0380 разрешено в Swift генерал-лейтенант if
и switch
Используемый в качестве выражения, он уменьшает количество шаблонов в вашем коде.
Основные преимущества:
if
и switch
в предложениииспользовать return
Ключевые слова.if
и switch
выражение Может Вложенныйсуществоватьдругойвыражениесередина,оти добиться большей гибкостиизкодструктура。// существовать Swift 5.1 До
func rating(for score: Int) -> String {
if score > 500 {
return "Pass"
} else {
return "Fail"
}
}
// существовать Swift 5.1 и более поздние версии
func rating(for score: Int) -> String {
score > 500 ? "Pass" : "Fail"
}
Что следует отметить:
if
и switch
Различные ветви выражения должны иметь один и тот же изтип.if
Условие в выражении должно быть логическим значением.пакет параметровпозволятьтыписатьиметь дело слюбое числотипиз Дженерикитипифункция。
Например, если нет пакета параметры, если вы хотите написать имя для all
функция проверки любого числа из Optional
ценитьданетдля nil
,Вам нужно для каждого отдельного человека, и вы хотите, чтобы длина параметра подтверждения прописывалась отдельно для каждого отдельного человека из-за перегрузки.,от при создании индивидуального произвольного из шапки:
func all<W1>(_ optional: W1?) -> W1?
func all<W1, W2>(_ optional1: W1?, optional2: W2?) -> (W1, W2)?
func all<W1, W2, W3>(_ optional1: W1?, optional2: W2?, optional3: W3?) -> (W1, W2, W3)?
Используя пакеты параметров, вы можете использовать это API выражатьдляодининдивидуальныйнет верхнего пределаизодининдивидуальныйфункция,позволять Вы передаете любое числоизпараметр:
func all<each Wrapped>(_ optional: repeat (each Wrapped)?) -> (repeat each Wrapped)?
Вызов API параметров использования пакета интуитивно понятен и не требует дополнительных действий:
if let (int, double, string, bool) = all(optionalInt, optionalDouble, optionalString, optionalBool) {
print(int, double, string, bool)
}
else {
print("got a nil")
}
Метод многопараметрической записи также поддерживается:
func pairUp3<each T: WritesFrontEndCode, each U: WritesBackEndCode>(firstPeople: repeat each T, secondPeople: repeat each U) -> (repeat (first: each T, second: each U)) {
return (repeat (each firstPeople, each secondPeople))
}
SE-0382, SE-0389, SE-0397 для Swift Привез Макрос. Макросда Мощный инструмент, позволяющий создавать существующие исходные коды преобразования во время компиляции.
Ключевые выводы:
ExpressionMacro
ииспользуется длядобавить в getter и setter из AccessorMacro
,ConformanceMacro
используется дляделатьтипсоответствоватьпротокол。использовать Макросизшаг:
использовать Макрос:
Но и:
Например Во-первых, нам нужно создать код, который выполняет расширение Макроса из - put #buildDate
стать похожим на 2023-06-05T18:00:00Z из вещей:
public struct BuildDateMacro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
let date = ISO8601DateFormatter().string(from: .now)
return "\"\(raw: date)\""
}
}
ВАЖНОЕ ПРИМЕЧАНИЕ: Этот код не должен находиться в цели вашего основного приложения; мы не хотим, чтобы этот код был скомпилирован в наше окончательное приложение, мы просто хотим, чтобы он содержал окончательную строку даты. В этом же модуле мы создаем CompilerPlugin
протоколизструктура,Экспортируйте нас из Макрос:
import SwiftCompilerPlugin
import SwiftSyntaxMacros
@main
struct MyMacrosPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
BuildDateMacro.self
]
}
Затем,нас Воля Чтодобавить в прибыть Package.swift
В списке целей:
.macro(
name: "MyMacrosPlugin",
dependencies: [
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
На этом создание Макроса во внешнем модуле завершено. Остальная часть нашего кода используется везде, где мы хотим использовать Макросимерсуществовать в пределах нашей основной цели приложения. Для этого необходимо сделать два шага: сначала определить, что такое Макросда. В нашем примере этот даиндивидуальный вернет отдельную строку из автономного выражения Макрос, которая хранится в существовании. MyMacrosPlugin
модуль и имеет строгое имя BuildDateMacro
。поэтому,нас Воляэто определениедобавить в прибытьнасизглавная цель:
@freestanding(expression)
macro buildDate() -> String =
#externalMacro(module: "MyMacrosPlugin", type: "BuildDateMacro")
Второй шаг индивидуального даактуального использования Макроса заключается в следующем:
print(#buildDate)
Когда вы читаете этот код,Самый важный урожайда,основнойиз Макрос Функция——BuildDateMacro Весь код в структуре - существования запускается во время сборки, и его результаты внедряются на сайт вызова. Поэтому выше мы print() Вызов будет переписан примерно так:
print("2023-06-05T18:00:00Z")
Снова Например,Попробуйте индивидуальный более полезный из Макрос,На этот раз я сделал индивидуальный атрибут участника Макрос. Применительно к типу (классу – например),этотпозволятьнасвернодобрыйсерединаиз Каждыйиндивидуальный Заявление участникаодининдивидуальныйсвойство.этотистаршеиз @objcMembers
Атрибут существование концептуально аналогичен даиз. @objc
добавить в прибытьтипсерединаиз Каждыйиндивидуальныйсвойство. Например,еслитыиметьодининдивидуальныйиспользовать @Published
Каждое индивидуальное свойство существования наблюдаемого объекта можно написать простое индивидуальное из @AllPublished
Макросприходите, чтобы закончить эту Работу. Сначала напишем сам Макрос:
public struct AllPublishedMacro: MemberAttributeMacro {
public static func expansion(
of node: AttributeSyntax,
attachedTo declaration: some DeclGroupSyntax,
providingAttributesFor member: some DeclSyntaxProtocol,
in context: some MacroExpansionContext
) throws -> [AttributeSyntax] {
[AttributeSyntax(attributeName: SimpleTypeIdentifierSyntax(name: .identifier("Published")))]
}
}
Во-вторых, включите его в список существующих ваших поставщиков:
struct MyMacrosPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
BuildDateMacro.self,
AllPublishedMacro.self,
]
}
третий,существованиевы из основной цели приложения заявил Макрос,этот Второсортный Воля Чтоотметкадлядополнительные членысвойство Макрос:
@attached(memberAttribute)
macro AllPublished() = #externalMacro(module: "MyMacrosPlugin", type: "AllPublishedMacro")
Теперь существуетиспользовать его, чтобы аннотировать ваш наблюдаемый класс объектов:
@AllPublished class User: ObservableObject {
var username = "Taylor"
var age = 26
}
Мы можем принять параметры для управления их поведением, но сложность может легко резко возрасти.
Swift член командыподдерживать ПонятноодининдивидуальныйГитхаб, есть некоторые macro из Пример。
SE-0390 представлять Потрясающийкопироватьизструктурателои перечисление。Напротив, оно можеткопироватьизструктурателои перечислениеизодининдивидуальный Экземпляры могутсуществоватьмногоиндивидуальныйобмен местами——Хотясуществоватькодкаждыйиндивидуальныйместный визит,Но в итоге владелец все равно один.
первый,этотвнутрипредставлять Понятноодининдивидуальныйновый синтаксис:~Copyable
。этотиндивидуальныйграмматикадаодинтелоиз,Других на данный момент нетиз~Equatable
картинаизграмматика。
поэтомунассоздаватьодининдивидуальный НеткопироватьUser
структуратело:
struct User: ~Copyable {
var name: String
}
Уведомление:Неткопироватьтип,Нетспособный СноваследоватьSendable
снаружиизпротокол。
Как только мы заявим, что для не копировать,Хорошо, все будет иначе, чем раньше,Например, ниже
func createUser() {
let newUser = User(name: "Anonymous")
var userCopy = newUser
print(userCopy.name)
}
createUser()
Вроде бы нет никакой разницы между вышеописанными методами, но мы имеем User Объявление структуры для невозможно скопировать - как это можно скопировать newUser
Что? Ответ: не может: будет? newUser
назначен на userCopy
приведет к оригинальному из newUser
Значение потребляется (consume), а это означает, что оно больше не может быть использовано, поскольку для владения теперь принадлежит существующему. userCopy
。еслитыпытаться Воля print(userCopy.name)
Изменить для print(newUser.name)
,тыбуду читатьприезжать Swift выдать ошибку компилятора - этотда Нетпозволятьиз。
SE-0377 Также имеются новые ограничения при преобразовании копироватьтип для параметров функции:
consuming
。иметь в видуфункция После звонка,Исходное значение будет недействительным.borrowing
,и Другие заемщики читают стоимость вместе,следующее.func createAndGreetUser() {
let newUser = User(name: "Anonymous")
greet(newUser)
print("Goodbye, \(newUser.name)")
}
func greet(_ user: borrowing User) {
print("Hello, \(user.name)!")
}
createAndGreetUser()
если мы позволим greet()
Использование функции consuming User
,но Нетпозволять print("Goodbye, (newUser.name)")
- Swift узнаетдлясуществовать greet()
После запуска newUser
ценитьневерный。Другойодинаспект,Из-за метода для потребления должен завершиться жизненный цикл объекта.,Таким образом, они могут свободно менять свою собственность.
этотобщая строкадлядарить Потрясающийкопироватьизструктурателоодиндобрый суперспособность,И эта суперсила раньше была ограничена классами и акторами: мы можем предоставить им деструкторы.,Когда уничтожается последняя ссылка на недоступный экземпляр человека,Он запустится автоматически.
Важное примечание: Это немного отличается от деструктора класса, что может быть незначительной проблемой в ранних реализациях.
первый,Этот класс можно использовать из деструктора из кода:
class Movie {
var name: String
init(name: String) {
self.name = name
}
deinit {
print("\(name) is no longer available")
}
}
func watchMovie() {
let movie = Movie(name: "The Hunt for Red October")
print("Watching \(movie.name)")
}
watchMovie()
Когда он запускается, он печатает «Наблюдение The Hunt for Red Октябрь», затем напечатайте «The Hunt for Red October is no longer доступен». Но если определить типизот class Movie
Изменить для struct Movie: ~Copyable
,тыбуду читатьприезжатьэтотдваиндивидуальный print() Операторы выполняются в обратном порядке.
Недоступно в рамках копировать типизметод по умолчанию дазаимствованныйиз,ноэтоих Можеткартина Можеткопироватьтиподин Образецотметкадляmutating
переменнаяиз,И они также могут быть помечены как расходные материалы.,выражать Долженценитьсуществоватьметод После запусканеверный。
struct MissionImpossibleMessage: ~Copyable {
private var message: String
init(message: String) {
self.message = message
}
consuming func read() {
print(message)
}
}
Это помечает само сообщение как личное, поэтому его можно использовать только путем вызова экземпляра из read()
способ посетить его.
В отличие от переменного метода,расходный материалметод Можетсуществоватьтипизпостоянныйlet
Запуск на экземпляре。поэтому,такизкодда Можетиз:
func createMessage() {
let message = MissionImpossibleMessage(message: "You need to abseil down a skyscraper for some reason.")
message.read()
}
createMessage()
Уведомление: потому чтодля message.read()
Экземпляр сообщения использован, поэтому попробуйте позвонить еще раз. message.read()
даошибкаиз。
В сочетании с деструкторомиспользуйте,Расходный метод станет сложнее,Потому что они могут повторить любую уборку, которую вы делаете. Например,Если вы существуете, следите за рекордами в игре,ты Можетспособный希望иметьодининдивидуальныйрасходный материализ finalize()
метод,Запишите последний рекорд в постоянное хранилище и не позволяйте другим в дальнейшем изменять этот результат.,Но у вас также может быть деструктор,Сохраните последний результат на диск, когда объект будет уничтожен.
для Чтобы избежать этой индивидуальной проблемы, Swift 5.9 представлять Понятноодининдивидуальныйновыйиз discard
Оператор, который можно использовать для потребляемого метода, который не является копиртипизацией. Когда вы существуете, расходный методсерединаиспользовать discard self
, это не позволяет запустить деструктор для этого объекта.
поэтому,нас Можеттак Реальностьсейчаснасиз HighScore Структура:
struct HighScore: ~Copyable {
var value = 0
consuming func finalize() {
print("Saving score to disk…")
discard self
}
deinit {
print("Deinit is saving score to disk…")
}
}
func createHighScore() {
var highScore = HighScore()
highScore.value = 20
highScore.finalize()
}
createHighScore()
Совет: Когда этот код запустится, вы увидите сообщение деструктора, напечатанное дважды. - однажды, когда мы меняемся value
свойство, это фактически уничтожает и воссоздает структуру, а в другой раз, когда createHighScore()
В конце метода.
существоватьиспользовать Когда дело доходит до новых функций, необходимо учитывать некоторые дополнительные сложности:
consume
оператор дляконецпривязка переменныхизжизненный циклSE-0366 Воляconsume
Расширяется докопироватьтипизлокальные переменныеипостоянный,этот Можетспособныйиметь利于那некоторый希望避免существоватьданныепроцесс передачисерединаэкранназадслучилосьмного retain/release Звоните разработчикам.
Оператор потребления выглядит следующим образом:
struct User {
var name: String
}
func createUser() {
let newUser = User(name: "Anonymous")
let userCopy = consume newUser
print(userCopy.name)
}
createUser()
Среди них линия критических темпов – да let userCopy
Хорошо, он делает две вещи одновременно:
newUser
копироватьприезжать userCopy
。newUser
изжизненный цикл,поэтомулюбой Входитьодин步пытаться访问этобудет брошеношибка。Это позволяет нам явно сказать компилятору: «Не позволяйте мне снова использовать это значение».
Более распространенные способы сделать да,использовать_
Воля Что投入 black дыра - я не хочу копировать данные, я просто хочу пометить их как уничтоженные. похожий:
func consumeUser() {
let newUser = User(name: "Anonymous")
_ = consume newUser
}
Или используйте его при передаче значений в функции:
func createAndProcessUser() {
let newUser = User(name: "Anonymous")
process(user: consume newUser)
}
func process(user: User) {
print("Processing \(name)…")
}
createAndProcessUser()
У этой черты есть два дополнительных момента, о которых стоит знать.
Во-первых, Свифт Отслеживайте, какие ветки вашего кода потребляют значения и условно выполняют правила. Поэтому в этом коде нас поглощает только одна из двух возможностей. User
Пример:
func greetRandomly() {
let user = User(name: "Taylor Swift")
if Bool.random() {
let userCopy = consume user
print("Hello, \(userCopy.name)")
} else {
print("Greetings, \(user.name)")
}
}
greetRandomly()
Во-вторых,от Технически,consume
Держатьделатьиздаобязательностьи Нетдаценить。на самом деле,Это означает, что если мы используем переменную,Мы можем повторно инициализировать переменную и нормализовать ее:
func createThenRecreate() {
var user = User(name: "Roy Kent")
_ = consume user
user = User(name: "Jamie Tartt")
print(user.name)
}
createThenRecreate()
AsyncStream
и AsyncThrowingStream
Удобство makeStream
методSE-0388 для AsyncStream
и AsyncThrowingStream
добавить водининдивидуальныйновыйиз makeStream()
метод,Долженметод Возвращает как сам поток, так и его продолжение.(continuation
)。
Поэтому нам больше не нужно писать такой код:
var continuation: AsyncStream<String>.Continuation!
let stream = AsyncStream<String> { continuation = $0 }
Теперь мы можем получить оба:
let (stream, continuation) = AsyncStream.makeStream(of: String.self)
Это существование требует, чтобы существование было доступно вне текущего контекста, где продолжение особенно популярно.,Напримерсуществовать Неттакой жеизметодсередина。Например,Раньше мы могли бы написать простой генератор чисел,так,Требуется продолжение для хранения атрибута для собственного из.,для того, чтобы иметь возможность queueWork()
Он вызывается в методе:
struct OldNumberGenerator {
private var continuation: AsyncStream<Int>.Continuation!
var stream: AsyncStream<Int>!
init() {
stream = AsyncStream(Int.self) { continuation in
self.continuation = continuation
}
}
func queueWork() {
Task {
for i in 1...10 {
try await Task.sleep(for: .seconds(1))
continuation.yield(i)
}
continuation.finish()
}
}
}
использоватьновыйиз makeStream(of:)
метод, этот код становится еще проще:
struct NewNumberGenerator {
let (stream, continuation) = AsyncStream.makeStream(of: Int.self)
func queueWork() {
Task {
for i in 1...10 {
try await Task.sleep(for: .seconds(1))
continuation.yield(i)
}
continuation.finish()
}
}
}
Clock
добавить в sleep(for:)
методSE-0374 для Swift из Clock
протоколдобавить С новым методом расширения мы лучше приостанавливаем выполнение на указанное количество секунд, а также продлеваем его в зависимости от продолжительности. Task
Спите с особым одобрением толерантности.
Clock
Изменения небольшие, но очень специфичные, особенно если смоделировать их во время тестирования. Clock
Пример устранения задержек, которые в противном случае существовали бы в производственных средах.
Например, эту отдельную категорию можно использовать с любым типом. Clock
Создайте и используйте это перед запуском операции сохранения. Clock
Спать:
class DataController: ObservableObject {
var clock: any Clock<Duration>
init(clock: any Clock<Duration>) {
self.clock = clock
}
func delayedSave() async throws {
try await clock.sleep(for: .seconds(1))
print("Saving…")
}
}
потому чтодляэтоиспользовать Понятно any Clock<Duration>
,таксейчассуществовать Можетсуществоватьпроизводственная средасерединаиспользоватькартина ContinuousClock
Таково из вещей, но существование проверь себя из DummyClock
,существовать那внутриты Можетпренебрегатьвсеизsleep()
команда для быстрого выполнения тестов.
существоватьстарая версияиз Swift , теоретический эквивалент кода да try await clock.sleep(until: clock.now.advanced(by: .seconds(1)))
,Но в данном примере это не работает,потому чтодля clock.now
Недоступно, потому что для Swift Я не знаю, какой именно типиз Clock。
Что касается права Task
Сон меняется, это означает, что мы можем написать такой код:
try await Task.sleep(until: .now + .seconds(1), tolerance: .seconds(0.5))
Упроститьдля:
try await Task.sleep(for: .seconds(1), tolerance: .seconds(0.5))
SE-0381 добавить вновыйиз Может Отменить группу задача, исправил текущую API Дефект в индивидуальном важном: существование. Задачи, созданные в группе задач, будут автоматически отброшены и уничтожены после завершения, что означает, что они будут выполняться в течение длительного времени (или как Web Таким образом, сервер может работать вечно) из групп задач не будет происходить утечка памяти с течением времени.
использоватьоригинальныйиз withTaskGroup()
API индивидуальная проблема может возникнуть, потому что для Swift только когда мы звоним next()
или Просмотрите группу задач из подзадач, прежде чем отбрасывать подзадачи и их результаты. Если все подзадачи в данный момент выполняются, вызовите next()
приведет к приостановке выполнения кода, поэтому у нас есть проблема: вы хотите, чтобы сервер всегда прослушивал соединения, чтобы он мог добавить в задачах для их обработки, но вам также придется время от времени останавливать и очищать старые задачи, которые были выполнены.
существовать Swift 5.9 До,этотиндивидуальный Проблема не чистаяизрешение。Swift 5.9 добавить в withDiscardingTaskGroup()
и withThrowingDiscardingTaskGroup()
Функция создания новых из группу задача. Эти группы задач будут автоматически отбрасывать и уничтожать каждую отдельную задачу после ее завершения, без необходимости вызывать ее вручную. next()
потреблять его.
для Сообщите вам, что вызывает эту проблемуиз,Мы можем реализовать простой монитор каталогов,это永远цикли报告добавить вили Удалитьизлюбой файлили Оглавлениеизимя:
struct FileWatcher {
// The URL we're watching for file changes.
let url: URL
// The set of URLs we've already returned.
private var handled = Set<URL>()
init(url: URL) {
self.url = url
}
mutating func next() async throws -> URL? {
while true {
// Read the latest contents of our directory, or exit if a problem occurred.
guard let contents = try? FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil) else {
return nil
}
// Figure out which URLs we haven't already handled.
let unhandled = handled.symmetricDifference(contents)
if let newURL = unhandled.first {
// If we already handled this URL then it must be deleted.
if handled.contains(newURL) {
handled.remove(newURL)
} else {
// Otherwise this URL is new, so mark it as handled.
handled.insert(newURL)
return newURL
}
} else {
// No file difference; sleep for a few seconds then try again.
try await Task.sleep(for: .microseconds(1000))
}
}
}
}
Тогда мы сможем существовать индивидуально, просто используя его.,для краткости,нас Только Распечатать URL Без выполнения какой-либо сложной обработки:
struct FileProcessor {
static func main() async throws {
var watcher = FileWatcher(url: URL(filePath: "/Users/twostraws"))
try await withThrowingTaskGroup(of: Void.self) { group in
while let newURL = try await watcher.next() {
group.addTask {
process(newURL)
}
}
}
}
static func process(_ url: URL) {
print("Processing \(url.path())")
}
}
Это будет работать вечно,Или, по крайней мере, до тех пор, пока пользователь не завершит работу программы и каталог, который мы отслеживаем, больше не будет доступен. Но да,потому чтодляэтоиспользовать Понятно withThrowingTaskGroup()
,такиметьодининдивидуальныйвопрос:Каждый Второсортныйвызов addTask()
Новая подзадача создается каждый раз, а потому для она нигде не вызывается group.next()
,Таким образом, эти подзадачи никогда не уничтожаются. Этот код будет постепенно съедать все больше и больше памяти (возможно, всего несколько сотен байт за раз).,До финальной версии операционной системы из RAM Измучен, вынужден прекратить программу.
использовать Отменить группу задача может полностью решить эту индивидуальную задачу: просто добавьте withThrowingTaskGroup(of: Void.self)
Заменить для withThrowingDiscardingTaskGroup
,Каждая подзадача будет автоматически уничтожена после ее завершения.
на самом деле,Эта отдельная проблема в основном возникает в существующем серверном коде.,Сервер должен иметь возможность принимать новые соединения.,Также плавно обрабатывайте существующие соединения.
SE-0373 Расслабленныйсуществовать приводит к некоторым ограничениям при использовании переменных в построителе. Лучшее, о чем мы писали ранее, было бы запрещено компилятором изкода.
Например,существовать Swift 5.8 середина,Мы можем создать конструктор напрямую, используя ленивую переменную.,Как показано ниже:
struct ContentView: View {
var body: some View {
VStack {
lazy var user = fetchUsername()
Text("Hello, \(user).")
}
.padding()
}
func fetchUsername() -> String {
"@twostraws"
}
}
Это показывает индивидуальную концепцию,но не дает никаких преимуществ,потому чтодлявсего ленивых переменныхдаодеялоиспользовать——существовать Долженкодсерединаиспользовать lazy var
и let
никакой разницы。хотеть Понятноразвязатьэто Реальность际иметьиспользоватьизместонуждатьсяодининдивидуальныйдольшеизкод Пример,Как показано ниже:
// Пользователи — это активные подписчики, неактивные подписчики, и мы пока не знаем их статуса.
enum UserState {
case subscriber, nonsubscriber, unknown
}
// о Два небольших сообщения от пользователя из
struct User {
var id: UUID
var username: String
}
struct ContentView: View {
@State private var state = UserState.unknown
var body: some View {
VStack {
lazy var user = fetchUsername()
switch state {
case .subscriber:
Text("Hello, \(user.username). Here's what's new for subscribers…")
case .nonsubscriber:
Text("Hello, \(user.username). Here's why you should subscribe…")
Button("Subscribe now") {
startSubscription(for: user)
}
case .unknown:
Text("Sign up today!")
}
}
.padding()
}
// Пример функции, выполнит сложную работу
func fetchUsername() -> User {
User(id: UUID(), username: "Anonymous")
}
func startSubscription(for user: User) {
print("Starting subscription…")
}
}
Этот метод решает проблему, возникающую в альтернативе существования:
lazy
,Так fetchUsername()
Волясуществовать state извсетри видаслучай Всеодеяловызов,Прямо сейчасделатьсуществоватьодиндобрыйслучайэто没иметьодеялоиспользовать。lazy
и будет fetchUsername()
изпозвонить и поставитьсуществоватьдваиндивидуальный case середина,Затем мы будем копировать код — для простого однострочного кода это не большая проблема.,Но вы можете себе представить, насколько это будет сложнее из-за проблем в вашем коде.user
Перемещено в вычисляемое свойство, затем оно будет вызываться снова, когда пользователь нажимает кнопку «Подписаться сейчас».Это изменение такжепозволятьнассуществоватьконструктор результатовсерединаиспользоватьсвойствооберткаиместный расчетсвойство,Хотя я подозреваю, что они не очень полезны. Например,сейчассуществоватьпозволятьнижетипизкод:
struct ContentView: View {
var body: some View {
@AppStorage("counter") var tapCount = 0
Button("Count: \(tapCount)") {
tapCount += 1
}
}
}
Но да, хотя это приведет к UserDefaults
Значение меняется при каждом щелчке мыши, но используется таким образом @AppStorage
приводит не к каждому tapCount
При изменении body
Свойства отзываются - мы из UI Не будет автоматически обновляться для отражения изменений. Автоматически обновляется с учетом изменений.
SE-0376 добавить водининдивидуальныйновыйиз @backDeployed
Атрибут, это разрешено вновую версию изframeworkиспользоватьновую API. По принципу работы он записывает код функции в двоичный файл вашего приложения, затем выполняет проверку во время выполнения: если ваша пользовательская версия операционной системы достаточно новая, тогда будет использовать собственную версию функции системы, иначе буду использоватькопировать в двоичную версию вашего приложения.
Apple предоставляет некоторые новые функции в более ранних операционных системах в порядке ретроспективы.,Но я признаюдляэтоти Нетда Чтопанацея——@backDeployed
Доступно только для функции, метода, индекса и вычисляемых свойств, поэтому, хотя он может идеально подойти для небольших API измениться, например iOS 16.1 серединапредставлятьиз fontDesign()
модификатор, но он не применяется ни к одному коду, требующему использования новой типизации, например, зависит от новой ScrollBounceBehavior
Структура из нового scrollBounceBehavior() модификатор.
Например,iOS 16.4 для Text представлять Понятно monospaced(_ isActive:)
. Если это использует `@backDeployed`,SwiftUI Команды могут убедиться, что модификатор доступен для подтверждения, которое им действительно необходимо для реализации кода, как можно скорее. SwiftUI версия следующая:
extension Text {
@backDeployed(before: iOS 16.4, macOS 13.3, tvOS 16.4, watchOS 9.4)
@available(iOS 14.0, macOS 11, tvOS 14.0, watchOS 7.0, *)
public func monospaced(_ isActive: Bool) -> Text {
fatalError("Implementation here")
}
}
еслитак Реальностьсейчасмодификатор,существоватьвремя выполнения Swift буду использовать SwiftUI из Системной копии, если у нее уже есть этот модификатор, в противном случае используйте обратное развертывание из предыдущей версии. iOS 14.0 Похожие версии. На самом деле, хотя это и не открывает ничего нового, похоже, что ретроактивное развертывание — простой вариант, но мы не знаем. SwiftUI существуют внутреннеиспользоватьwhattype, поэтому трудно предсказать, что можно, а что нельзя развернуть задним числом. существоватьвнутреннийиспользовать Чтотип,Поэтому сложно предсказать, что можно будет развернуть назад.,Что запрещено.
weak self
После развязывания,поддерживать Скрытыйself
SE-0365 проходитьразрешено в слабый self
Capture распаковывается изplaceuse неявно self
Кпозволятьнасотзакрытиесередина Удалять `self。
class TimerController {
var timer: Timer?
var fireCount = 0
init() {
timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in
guard let self else { return }
print("Timer has fired \(fireCount) times")
fireCount += 1
}
}
}
существоватьSwift 5.8 доиз Версия,должениспользоватьself.fireCount
。
SE-0274упрощать Понятно #file
приходит магический идентификаториспользоватьModule/Filename,НапримерMyApp/ContentView.swift。до#file
Содержит полныйизпуть,если/Users/xxx/Desktop/yyy/ContentView.swift。 Оно очень длинное и содержит некоторую личную информацию.
Если вы хотите использовать старый полный путь,Можетиспользовать#filePath
。
// New behavior, when enabled
print(#file)
// Old behavior, when needed
print(#filePath)
Уведомление:В настоящее время эта индивидуальная функция не включена по умолчанию.,SE-0362добавлять Понятноиндивидуальныйодининдивидуальный-enable-upcoming-feature
изскомпилировать флаги для включенияновыйхарактеристика,Если вам нужна эта индивидуальная функция,Можетиспользовать-enable-upcoming-feature ConciseMagicFile
。
SE-0375Расширять Понятно Swift 5.7 из Функция, которую лучше использовать в протоколе, вызывает функцию generics, благодаря которой исправлена небольшая, но раздражающая несогласованность: Swift 5.7 Не отдавайте предпочтение дополнительному типу использования этой поездки, но Swift 5.8 тогда разрешили.
подиз Нет Можетвыбирать`T` функциясуществоватьSwift Его можно нормально использовать в 5.7.
func double<T: Numeric>(_ number: T) -> T {
number * 2
}
let first = 1
let second = 2.0
let third: Float = 3
let numbers: [any Numeric] = [first, second, third]
for number in numbers {
print(double(number))
}
В Swift 5.8 также можно использовать необязательные параметры.
func optionalDouble<T: Numeric>(_ number: T?) -> T {
let numberToDouble = number ?? 0
return numberToDouble * 2
}
let first = 1
let second = 2.0
let third: Float = 3
let numbers: [any Numeric] = [first, second, third]
for number in numbers {
print(optionalDouble(number))
}
В приведенном выше коде Swift 5.7 сообщит об ошибке «Введите «любой числовой» не может соответствовать «числовому».
Swift 5.8развязать决Понятно Досуществоватьопределенныйнекоторыйслучай Нетпозволять Кастинг коллекции——Например Воля ClassA
Приведение массива для унаследовано от ` ClassA` из Другой вид типизации массива.
class Pet { }
class Dog: Pet {
func bark() { print("Woof!") }
}
func bark(using pets: [Pet]) {
switch pets {
case let pets as [Dog]:
for pet in pets {
pet.bark()
}
default:
print("No barking today.")
}
}
существовать До,этотвнутри会报错“Collection downcast in cast pattern is not implemented; use an explicit downcast to '[Dog]' instead.”。 нуждатьсяиспользоватьif let dogs = pets as? [Dog] {
из Грамматики подойдет.
if let
快捷развязать Можетвыбирать包SE-0345 в правительстве появился новый синтаксис сокращений для использования if let
и guard let
Распакуйте необязательные значения в одноимённые теневые переменные. Это означает, что теперь мы можем написать такой код:
var name: String? = "Linda"
if let name {
print("Hello, \(name)!")
}
это изменение Нетподходящийиспользуется дляобъект Внутриизсвойство,этотиметь в видутакизкод Воля Не могущий Работа:
структура пользователя {
имя переменной: String
}
пусть пользователь: Пользователь? = Пользователь(имя: «Линда»)
// Начальная работа
если пусть user.name {
print("Добро пожаловать, \(user.name)!")
}
SE-0326 значительно улучшено Swift существоватьзакрытиесерединаиспользоватьпараметритипсделать выводизспособность,делатьпридетсясуществовать Можетмногослучай,нас Нет необходимости явно указывать вводивыходтип.этотделатьпридетсякод Более краткий,Легче читать.
Swift 5.7 можно без ошибок написать так:
let scores = [100, 80, 85]
let results = scores.map { score in
if score >= 85 {
return "\(score)%: Pass"
} else {
return "\(score)%: Fail"
}
}
В предыдущих версиях необходимо было четко указать тип возвращаемого значения:
let oldResults = scores.map { score -> String in
if score >= 85 {
return "\(score)%: Pass"
} else {
return "\(score)%: Fail"
}
}
SE-0329 у правительства есть новый, стандартизированный способ цитирования Swift Среднее по времени и продолжительности. Как следует из названия, он разделен на три основных компонента:
для многих людей,Самым простым из приложений будет недавно обновленный Task API.,Теперь можно указать время сна более разумным способом, чем наносекунды:
try await Task.sleep(until: .now + .seconds(1), clock: .continuous)
этотиндивидуальныйновыйиз API Также дает возможность указать уровень допуска, который позволяет системе немного подождать после крайнего срока перехода в режим сна, чтобы максимизировать энергоэффективность. Так что если мы хотим хотя бы поспать 1 секунд, но, надеюсь, продлится в общей сложности 1.5 Секунды, мы бы написали:
try await Task.sleep(until: .now + .seconds(1), tolerance: .seconds(0.5), clock: .continuous)
Совет: этот допуск только увеличивает время сна по умолчанию, исходя из того, что система существует как минимум после 1. секунды.
Хотя этого еще не произошло, похоже, что старый API на основе nanoсекунды в ближайшем будущем будет заменен на Устарело.
Часы также полезны для измерения чего-то конкретного, что очень полезно, если вы хотите показать пользователям, например, сколько времени потребовалось для экспорта файла:
let clock = ContinuousClock()
let time = clock.measure {
// complex work here
}
print("Took \(time.components.seconds) seconds")
Swift 5.7 имеет ряд улучшений, связанных с регулярными выражениями.,极大земля改Входить Понятнонасиметь дело снитьиз Способ。
/.../
без прохождения Regex
Возможность создания регулярных выражений строк. первый,насиметь Понятноодиннекоторыйновыйизнитьиметь дело сметод:
let message = "the cat sat on the mat"
print(message.ranges(of: "at"))
print(message.replacing("cat", with: "dog"))
print(message.trimmingPrefix("the "))
Мощная идея: мы можем использовать регулярные выражения:
print(message.ranges(of: /[a-z]at/))
print(message.replacing(/[a-m]at/, with: "dog"))
print(message.trimmingPrefix(/The/.ignoresCase()))
Кроме regex Литералы, Свифт Также предоставляет индивидуальное специализированное Regex
тип.
do {
let atSearch = try Regex("[a-z]at")
print(message.ranges(of: atSearch))
} catch {
print("Failed to create regex")
}
Есть одно ключевое отличие:
поэтомунас Можетнравиться下использовать,Буквально из режима.
let search1 = /My name is (.+?) and I'm (\d+) years old./
let greeting1 = "My name is Taylor and I'm 26 years old."
if let result = try? search1.wholeMatch(in: greeting1) {
print("Name: \(result.1)")
print("Age: \(result.2)")
}
Уведомление,result
из tuple,Можетпроходить.1
и.2
из Способ Приходить Цитироватьнасизсоответствовать(.0
выражатьвсеиндивидуальныйсоответствоватьизнить)
Мы даже можем назвать совпадения:
let search2 = /My name is (?<name>.+?) and I'm (?<age>\d+) years old./
let greeting2 = "My name is Taylor and I'm 26 years old."
if let result = try? search2.wholeMatch(in: greeting2) {
print("Name: \(result.name)")
print("Age: \(result.age)")
}
Swift Можно даже передать такой метод, как SwiftUI из DSL из СпособсоздаватьRegex。
import RegexBuilder
let search3 = Regex {
"My name is "
Capture {
OneOrMore(.word)
}
" and I'm "
Capture {
OneOrMore(.digit)
}
" years old."
}
настраивать,МожетдаиспользоватьTryCapture
и НетдаCapture
,Сотрудничать с методом трансформации
let search4 = Regex {
"My name is "
Capture {
OneOrMore(.word)
}
" and I'm "
TryCapture {
OneOrMore(.digit)
} transform: { match in
Int(match)
}
" years old."
}
Даже совпадающие имена могут быть выполнены:
let nameRef = Reference(Substring.self)
let ageRef = Reference(Int.self)
let search5 = Regex {
"My name is "
Capture(as: nameRef) {
OneOrMore(.word)
}
" and I'm "
TryCapture(as: ageRef) {
OneOrMore(.digit)
} transform: { match in
Int(match)
}
" years old."
}
if let result = greeting1.firstMatch(of: search5) {
print("Name: \(result[nameRef])")
print("Age: \(result[ageRef])")
}
SE-0347 существования, общий параметр нашей функции существования использует значение по умолчанию.
func drawLotto2<T: Sequence>(from options: T = 1...49, count: Int = 7) -> [T.Element] {
Array(options.shuffled().prefix(count))
}
SE-0343 Давайте существовать в коде верхнего уровняuseuse concurrency код.
import Foundation
let url = URL(string: "https://hws.dev/readings.json")!
let (data, _) = try await URLSession.shared.data(from: url)
let readings = try JSONDecoder().decode([Double].self, from: data)
print("Found \(readings.count) temperature readings")
some
заявлениеSE-0341 Объявление разблокированного параметра существуетиспользовать some Способность может заменить некоторые простые из общих мест.
func isSorted(array: [some Comparable]) -> Bool {
array == array.sorted()
}
[some Comparable]
Тип параметра означает, что эта функция подходит для приложений, содержащих Comparable протоколизировать типизируемый элемент из массива, это эквивалентно общему коду синтаксического сахара:
func isSortedOld<T: Comparable>(array: [T]) -> Bool {
array == array.sorted()
}
Конечно, мы все еще можем писать более длинные и ограниченные расширения:
extension Array where Element: Comparable {
func isSorted() -> Bool {
self == self.sorted()
}
}
Этот упрощенный универсальный синтаксис означает, что мы больше не можем создавать более сложные ограничения, поскольку для синтетических универсальных параметров нет конкретных имен.
some
типSE-0328 Расширен диапазон непрозрачных результатов, которые можно использовать.
Например, теперь мы можем возвращать несколько отдельных непрозрачных типов одновременно:
import SwiftUI
func showUserDetails() -> (some Equatable, some Equatable) {
(Text("Username"), Text("@twostraws"))
}
Мы также можем возвращать непрозрачные типы:
func createUser() -> [some View] {
let usernames = ["@frankefoster", "@mikaela__caron", "@museumshuffle"]
return usernames.map(Text.init)
}
Можно даже вернуть функцию, которая при вызове сама возвращает непрозрачный тип:
func createDiceRoll() -> () -> some View {
return {
let diceRoll = Int.random(in: 1...6)
return Text(String(diceRoll))
}
}
protocol
Все МожетделатьдляжитьсуществоватьтипExistential Type
SE-0309 сильно расслаблен Swift существоватьпротоколиметь Self
илиассоциациятип Запрещено по запросуиспользоватьпротоколделатьдлятипизпредел,转Кодининдивидуальныйтолькона основеэтоих所Делатьизидентификациясвойствоилиметодограниченныйиз Модель。
Проще говоря, это означает, что следующий код становится законным:
let firstName: any Equatable = "Paul"
let lastName: any Equatable = "Hudson"
Equatable
это с Self
Запросить изпротокол,Это означает, что он предоставляет ссылку на конкретную функцию типизации, которая его использует. Например,Int
соответствовать Equatable
,таккогданасобъяснять 4 == 4
Когда мы на самом деле запускаем дасуществовать, человек принимает два отдельных целых числа и возвращает существование, когда они совпадают. true изфункция。
Swift Вы можете использовать что-то вроде func ==(first: Int, second: Int) -> Bool
нет функции для достижения этой индивидуальной функциональности, но она плохо масштабируется - 他ихнуждатьсяписать几十индивидуальныйэтот Образецизфункция Приходитьиметь дело слогическое значениеценить、нить、Массив и т. д.。поэтому,Equatable
протоколиметьодининдивидуальныйпохожийиз Требовать:func ==(lhs: Self, rhs: Self) -> Bool
。по-английски,Это означает, что «вы должны быть в состоянии принять два экземпляра одного и того же тидивидуального,и告诉我этоихданеттакой же." Это может быть целое число, строка, логическое значение или логическое значение. Equatable из любого другого типаиз двух индивидуальных экземпляров.
чтобы избежать этой индивидуальной проблемыипохожийизвопрос,в любое время Self внесейчассуществовать Swift 5.7 Доизпротоколсередина,Компилятор не предпочитает существование нашего кода, используйте его.,Например:
let tvShow: [any Equatable] = ["Brooklyn", 99]
от Swift 5.7 Для начала этот код дапозволятьиз, теперь предел существования отложен до тех пор, пока вы не попробуете существовать. Swift Необходимо фактически реализовать его ограничения из ситуации localusetypez. Это значит, что мы не можем писать firstName == lastName
,Потому что это похоже на то, что я сказал,==
Необходимо убедиться, что в нем есть два экземпляра одного и того же типа для «Работа» и «Использовать». any Equatable
Мы скрыли данные из точного типа.
Однако,Мы получаем возможность выполнять проверки данных во время выполнения.,Чтобы определить конкретный контент, с которым мы имеем дело. существующиеus из смешанного массиваиз случая,Мы можем написать такой код:
for item in tvShow {
if let item = item as? String {
print("Found string: \(item)")
} else if let item = item as? Int {
print("Found integer: \(item)")
}
}
или ВОЗсуществоватьнасиздваиндивидуальныйнитьизслучай,нас Можетиспользоватьэтотиндивидуальный:
if let firstName = firstName as? String, let lastName = lastName as? String {
print(firstName == lastName)
}
Поймите это индивидуальное изменение из ключа, чтобы запомнить его, и мы будем более свободно использовать этот протокол.,Пока мы не делаем ничего особенного, требующего понимания внутренностей типизаций. поэтому,нас Можетписатькод Приходитьисследоватьлюбойпоследовательностьсерединаизвсепроектданетсоответствовать Identifiable
протокол:
func canBeIdentified(_ input: any Sequence) -> Bool {
input.allSatisfy { $0 is any Identifiable }
}
Короче говоря, SE-0309 расслабленный Swift Для тех, у кого есть Self
илиассоциациятип Запросить изпротоколделатьдлятиписпользоватьиз лимита.этотделатьпридетсянас Можетболее свободноиспользоватьэтотнекоторыйпротокол,Пока мы не выполняем каких-либо конкретных задач, чтобы понять внутренние операции типа. так,Мы можем писать более гибкий код,И все это при сохранении безопасности.
SE-0346 для кавычек есть определенная ассоциация типизпротоколдобавить вновыйиз、Проще с точки зрения синтаксиса.
Например,Если мы пишем из кодированного кеша разные типизданные разными способами,Мы могли бы начать так:
protocol Cache<Content> {
associatedtype Content
var items: [Content] { get set }
init(items: [Content])
mutating func add(item: Content)
}
Уведомление,существование протокола выглядит как протокол, так и общий тип - у него есть родственный тип,Там говорится, что соответствиетипу необходимо заполнить какую-то дыру.,Но исуществоватьугловые скобкисередина列вне Понятно Должентип:Cache<Content>
。
из части да в угловых скобках Swift Обратитесь к его основной связанной типизационной части, чтобы в ближайшее время понять, что не все связанные типы должны быть объявлены там существующими. Вместо этого вам следует перечислить только те словарные ключи и значения, которые изтипили, которые обычно особенно интересуют вызывающий код. Identifiable
протокол из идентификатора типа.существовать в нашем примере, мы говорим, что мы из кеша из контента - Строки, изображения, пользователи и т. д. - это основной тип ассоциации.
в это время,Мы можем продолжать, как и раньше - мы можем создать некоторые кэши, которые нам нужны.,Затемсоздаватьодининдивидуальныйсоответствоватьпротоколиз Инструменттеломедленныйжитьтип,Как показано ниже:
struct File {
let name: String
}
struct LocalFileCache: Cache {
var items = [File]()
mutating func add(item: File) {
items.append(item)
}
}
Теперь существованиеда умное из раздела: При создании кэша,Очевидно, мы можем напрямую создать отдельный экземпляр из кэша.,Как показано ниже:
func loadDefaultCache() -> LocalFileCache {
LocalFileCache(items: [])
}
Но часто мы хотим скрыть конкретные действия, которые делаем.,Как показано ниже:
func loadDefaultCacheOld() -> some Cache {
LocalFileCache(items: [])
}
использовать some Cache
дает нам возможность изменить конкретный кеш, который мы отправляем обратно, но SE-0346 позволятьнас Делатьиздасуществовать Инструменттелотип绝верноидентификацияи Нетпрозрачный返回тип相когда模糊之间поставлятьодининдивидуальныйсерединапромежуточная зона。так,Мы можем специализировать протокол,Как показано ниже:
func loadDefaultCacheNew() -> some Cache<File> {
LocalFileCache(items: [])
}
поэтому,насоставаться Понятно Воля Приходить转移приезжать Неттакой жеиз Cache
-conforming Возможности типизации, но мы ясно дали понять, что независимо от того, что выбрано, существование будет храниться внутри.
Этот более разумный синтаксис распространяется и на другие места.,включатькартина Расширятьэтот Образециз вещей:
extension Cache<File> {
func clean() {
print("Deleting all cached files…")
}
}
èОбщие ограничения:
func merge<C: Cache<File>>(_ lhs: C, _ rhs: C) -> C {
print("Copying all files into a new location…")
// now send back a new cache with items from both other caches
return C(items: lhs.items + rhs.items)
}
Но самая полезная изда, SE-0358. Использование этих основных типов ассоциаций в Swift из стандартной библиотеки, поэтому Sequence
、Collection
Каждый получит выгоду - мы можем написать Sequence<String>
Запись не имеет ничего общего с точной последовательностью существованияиспользоватьизтипизкода.
Existential Type
SE-0353поставлять Понятнокомбинация SE-0309(всепротоколprotocol
Все МожетделатьдляжитьсуществоватьтипExistential Type
) и SE-0346(Упрощение однородных типов первичных ассоциаций) изспособность,Может写внеany Sequence<String>
。
actor
изоляцияSE-0336 и SE-0344 представлять Понятно actor Возможности работы в распределенной форме - использовать удаленный вызов процедуры (RPC) существовать Читайте и пишите в Интернете.свойствоиливызовметод。
Эта проблема настолько сложна, насколько вы себе представляете,ноиметь三点Можетделатьэто Изменятьпридется Полегче:
await
Вызывается, если actor случается далокализация, тогда звонок будет сделан как для обычного местного actor функцияиметь дело с。distributed actor
Затем distributed func
。поэтому,Мы могли бы написать такой код, чтобы имитировать отслеживание системы торговых карточек:
// use Apple's ClusterSystem transport
typealias DefaultDistributedActorSystem = ClusterSystem
distributed actor CardCollector {
var deck: Set<String>
init(deck: Set<String>) {
self.deck = deck
}
distributed func send(card selected: String, to person: CardCollector) async -> Bool {
guard deck.contains(selected) else { return false }
do {
try await person.transfer(card: selected)
deck.remove(selected)
return true
} catch {
return false
}
}
distributed func transfer(card: String) {
deck.insert(card)
}
}
Из-за распределенного actor Вызвав функцию изthrows, мы можем гарантировать, что существуют вызовы person.transfer(card:)
При удалении карты безопасности в Collector исключений не возникает.
Swift изTargetда позволяет легко разместить actor из Понятноразвязать Перейти к распределенному актер, но есть некоторые существенные различия, которые могут вас смутить.
первый,всераспределенныйфункциядолжениспользовать try
и await
Вызывается, даже если функция не отмечена для throwing,для Ошибка из-за сетевого вызова,Могут возникнуть неисправности.
Во-вторых,Все параметры распределенного метода и возвращаемые значения должны соответствовать выбранному вами процессу сериализации.,Например Codable
。этотсуществоватьвремя компиляциипридетсяприезжатьисследовать,поэтому Swift Могу гарантировать, что это возможно удаленно actor Отправляйте и получайте данные.
третий,тыотвечать Должен考虑调всетыиз actor API для минимизации запросов данных. Например, если вы хотите читать распределенно actor из username
、firstName
и lastName
свойство,Вы должны предпочесть использовать один индивидуальный метод для запроса всех трех индивидуальных атрибутов.,Вместо того, чтобы делать их отдельными запросами атрибутов,Во избежание возможного многократного существования в сети туда и обратно.
buildPartialBlock
SE-0348 Значительно упрощает реализацию сложных результатов builder требует перегрузки, что тоже да Swift Расширенные регулярные выражения поддерживаются, возможно, отчасти по этой причине. SwiftUI Если команда адаптируется к этой особенности, она теоретически будет устранена. SwiftUI только самое 10 индивидуальный view из лимита.
для дал вам практический пример, вот упрощенная версия SwiftUI из ViewBuilder
:
import SwiftUI
@resultBuilder
struct SimpleViewBuilderOld {
static func buildBlock<C0, C1>(_ c0: C0, _ c1: C1) -> TupleView<(C0, C1)> where C0 : View, C1 : View {
TupleView((c0, c1))
}
static func buildBlock<C0, C1, C2>(_ c0: C0, _ c1: C1, _ c2: C2) -> TupleView<(C0, C1, C2)> where C0: View, C1: View, C2: View {
TupleView((c0, c1, c2))
}
}
Создал два индивидуальных buildBlock()
из версии: Один индивидуальный принимает два индивидуальных представления, один индивидуальный принимает три индивидуальных представления. Фактически, SwiftUI Принимайте различные альтернативы, но ключ может быть только 10 индивидуальный。
Затем,Мы можем использовать функции или вычисляемые свойства для использования построителя результатов.,Как показано ниже:
@SimpleViewBuilderOld func createTextOld() -> some View {
Text("1")
Text("2")
Text("3")
}
этотбуду использовать buildBlock<C0, C1, C2>()
Варианты принимают все три индивидуальных Text
просмотреть и вернуть все индивидуальные файлы, содержащие их TupleView
。Однако,существоватьэтотиндивидуальныйупрощатьиз Примерсередина,ни за чтодобавить вчетвертыйиндивидуальный Text
Просмотрите, потому что для меня не предусмотрено больше из перегрузок, типа SwiftUI Нетподдерживать 11 индивидуальныеили больше того же самого.
Это даиз buildPartialBlock()
из действий, поскольку для него путь из Работы аналогичен последовательности из reduce()
метод: имеет начальное значение «индивидуальный», затем уже будет добавлено содержимое по в прибыть Следующийиз Внутри容Приходить更новый Долженценить。
так,Мы можем создать индивидуальный новый конструктор результатов,Он знает, как принять индивидуальную точку зрения,И как совместить этот вид с другим индивидуальным видом:
@resultBuilder
struct SimpleViewBuilderNew {
static func buildPartialBlock<Content>(first content: Content) -> Content where Content: View {
content
}
static func buildPartialBlock<C0, C1>(accumulated: C0, next: C1) -> TupleView<(C0, C1)> where C0: View, C1: View {
TupleView((accumulated, next))
}
}
Хотя мы принимаем только один индивидуальный или два индивидуальных мнения из вариантов.,Но поскольку для них накапливаются,насна самом деле Можетиспользоватьпроизвольныймногоиндивидуальный:
@SimpleViewBuilderNew func createTextNew() -> some View {
Text("1")
Text("2")
Text("3")
}
Результат будет не совсем одинаковым: сначала индивидуальный пример мы получим индивидуальный TupleView<Text, Text, Text>
,исейчассуществоватьнас Воляпридетсяприезжатьодининдивидуальный `TupleView<(TupleView<(Text, Text)>, Text)> `- одининдивидуальный TupleView
Вложенныйсуществовать Другойодининдивидуальный TupleView
Внутри.
tips: buildPartialBlock()
да Swift изодинчасть,и не является частью какой-либо конкретной среды выполнения платформы,Так что, если вы примете это,Вы обнаружите, что его можно развернуть в более ранних версиях операционной системы.
SE-0352 позволять Swift Во многих случаях использование протокола вызывает универсальную функцию.
Подниматьиндивидуальныйпример,Вот отдельная простая из общей функции.,способный够иметь дело слюбойтипиз Numeric
ценить:
func double<T: Numeric>(_ number: T) -> T {
number * 2
}
Если мы вызовем это напрямую, например double(5)
,Так Swift Компилятор может выбрать специализацию функции - Из соображений производительности эффективно создавайте отдельный акцепт напрямую. Int
из версии.
И SE-0352 изделатьиспользоватьдаразрешено в Мы знаем, что мы изданныесоответствовать протоколам случаев называем эту функцию, как показано ниже:
let first = 1
let second = 2.0
let third: Float = 3
let numbers: [any Numeric] = [first, second, third]
for number in numbers {
print(double(number))
}
Swift Сохраните эти имена как тип существования: Вы существуетеиспользоватьизактуально данн. йтип находится внутри отдельного ящика, и когда мы вызываем метод в этом ящике, Swift 理развязатьэтоотвечать Должен Скрытыйземлясуществоватьвнутри коробкиизданныеначальствовызовметод。SE-0352 также расширяет эту функциональность для вызовов функций.:насциклсерединаизnumber
ценитьдаодининдивидуальныйжитьсуществоватьтип(одининдивидуальный Включать Int
、Double
или Float
из коробки), но Swift Возможность передать его дженерикам double()
Функция, метод передаются в значении из поля.
Эта функциональность имеет свои ограничения. Например, этот код не может работать:
func areEqual<T: Numeric>(_ a: T, _ b: T) -> Bool {
a == b
}
print(areEqual(numbers[0], numbers[1]))
Swift Невозможно определить, можно ли использовать эти два отдельных значения во время статической проверки (т.е. существуют во время компиляции) ==
сравнивать,поэтомукод根本Не могущий构建。
SE-0340 Частично решено Swift и发Модельсередина Можетспособныйжитьсуществоватьизрискованная ситуация,лучше Мы типифунцию отметим для существования недоступно в асинхронном контексте,Потому что при таком использовании они могут вызвать проблемы. Если вы не используете локальное хранилище потоков, блокировки, мьютексы или семафоры.,В противном случае вы вряд ли станете владельцем этой недвижимости.,Но вы можете использовать этот атрибут из кода.,поэтому По меньшей мереценитьпридется Понятноразвязатьэтоизжитьсуществовать。
Чтобы пометить определенный контент как «существовать», недоступный в асинхронном контексте, пожалуйста, @available
и Обычно вы выбираете платформу, Затемсуществовать в конце добавить в noasync
。Например,У нас может быть индивидуальная функция для любой платформы.,Но существование может вызвать проблемы при асинхронном вызове.,поэтомунас Воляэтот Образецотметкаэто:
@available(*, noasync)
func doRiskyWork() {
}
Далее мы можем вызвать его как обычно из обычной функции синхронизации:
func synchronousCaller() {
doRiskyWork()
}
Однако,Если мы попытаемся использовать асинхронную функцию, выполните ту же операцию.,Swift Будет выдана ошибка, поэтому этот код не будет работать:
func asynchronousCaller() async {
doRiskyWork()
}
Эта защита является улучшением по сравнению с текущей ситуацией.,Но не стоит слишком на это полагаться,потому чтодляэто Нетпредотвратитнас Вложенныйвызовнасиз noasync
функция, как показано ниже:
func sneakyCaller() async {
synchronousCaller()
}
Это существование работает в асинхронном контексте,Но вызовите функцию синхронизации,Затем Может反过Приходитьвызов noasync
функция doRiskyWork()
。
any
ключевые словаSE-0335 представлять Понятноодининдивидуальныйновыйизключевые слова any
Отметить месторождение существующего типа(экзистенциального тип). Эту индивидуальную характеристику немного сложно понять, и она также предвещает swift Последующие версии из break change。
протоколпозволять Указываем набор требований, которым должен соответствоватьизтип,Напримерэтоихдолженхотеть Реальностьсейчасизметод。поэтомунасчастонасчастоэтот Что написать:
protocol Vehicle {
func travel(to destination: String)
}
struct Car: Vehicle {
func travel(to destination: String) {
print("I'm driving to \(destination)")
}
}
let vehicle = Car()
vehicle.travel(to: "London")
Мы также можем использовать существующую функциюгенерал-лейтенантпротокол в качестве общего ограничения.,Это значит, что мы можем написать любой типданныйизкод.Например, удовлетворяющий указанному протоколиз., подкодподходящийиспользуется длялюбойудовлетворитьVehicle
протоколизациялюбойтип:
func travel<T: Vehicle>(to destinations: [String], using vehicle: T) {
for destination in destinations {
vehicle.travel(to: destination)
}
}
travel(to: ["London", "Amarillo"], using: vehicle)
Когда приведенный выше код компилируется, Swift Может Восприятиеприезжатькодсерединавызов ПонятноCar
из travel( )
метод,Затем Можетоптимизациядляпрямойвызовtravel()
——Разновидность статического планирования, называемая для отправка)из процесса.
Вот еще один аналогичный способ:
let vehicle2: Vehicle = Car()
vehicle2.travel(to: "Glasgow")
нас任然создавать ПонятноCar
изструктура,Да ПучокэтожитьмагазиндляVehicle
。этот Неттолькодапростойодинизскрывать Понятноосновная информация,идаэтотиндивидуальныйVehicle
становиться Понятноодининдивидуальныйпозвони этодляжитьсуществоватьтип(existential type)Полныйновыйизтип:способный разместитьсоответствоватьVehicle
протоколизлюбойтипизлюбойценитьизновыйданныетип.
важный:житьсуществоватьтип(existential type)ипроходитьsome
ключевые словазаявлениеиз Нетпрозрачныйтип(opaque тип) разные. Непрозрачный тип (непрозрачный type) представляет собой неизвестный из, конкретный изтип, который удовлетворяет указанному ограничению из. Несмотря на то, что да неизвестно, компилятор да гарантирует, что весь индивидуум единообразен во всех областях, и исиспользовать такой же, как и тип. Отсюда и непрозрачный тип (opaque. тип) может обеспечить более надежную гарантию и оптимизацию.
настакже Можетсуществоватьфункциясерединаиспользоватьжитьсуществоватьтип(existential type),Например:
func travel2(to destinations: [String], using vehicle: Vehicle) {
for destination in destinations {
vehicle.travel(to: destination)
}
}
этотдобрый Способ Хотяидругойтиписпользовать Способоченькартина。Дафункциясерединаприниматьлюбойудовлетворитьиз`Vehicle`объект,Swift Невозможно выполнить описанную выше оптимизацию. Он может передавать только динамическое планирование (динамическое send)iz, поэтому он не так эффективен, как статическая отправка, описанная выше. Следовательно, Свифт в настоящий момент Версиясередина,Два варианта использования протоколизма очень похожи.,на самом делепомедленнееизжитьсуществовать Версияизфункцияпроще написать。
для решения этой индивидуальной проблемы с помощью Swift 5.6 дляжитьсуществоватьтип(existential type)представлять Понятноany
ключевые слова,поэтомунас Сразу Можеткодсерединапоказыватьиз指вне Понятножитьсуществоватьиз Влияние。Следовать заиз Версияесли Нетиспользовать Предупредит。существует Swift 6 из может сообщить об ошибке,Требуйте четкой маркировки,Следуйте следующему методу записи:
let vehicle3: any Vehicle = Car()
vehicle3.travel(to: "Glasgow")
func travel3(to destinations: [String], using vehicle: any Vehicle) {
for destination in destinations {
vehicle.travel(to: destination)
}
}
SE-0315представлять Понятнозаполнитель типаизконцепция,Определим тип существования, отображение указанного типа детали.,пусть остальноеизчастьпроходитьтипсделать вывод Приходить填充。типсделать выводизместоиспользовать_
。
заполнить типсуществовать Это полезно, когда компилятор может правильно определить только часть типа. Это может упростить написание типизаций.,И доступно только одобрение,_?
var results3: [_: [Int]] = [
"Cynthia": [],
"Jenny": [],
"Trixie": [],
]
нуждаться Уведомлениеизда,в настоящий моментзаполнитель типавозвращаться Нетподдерживатьфункциязнак,Даты Может先写начальство_
заполнитель,Пусть компилятор сделает вывод,Затемпозволять Xcode Предоставьте предложения по ремонту для завершения кода.
String
/Int
изDictionary
также можетиспользоватьKeyedContainer
SE-0320 представлять Понятноодининдивидуальныйновыйиз CodingKeyRepresentable
протокол, предпочтение будет иметь необычный String или Int ключизсловарная кодировкадляключконтрольный контейнер,и Нетда Нетключконтрольный контейнер。этотразвязать决Понятнодосуществоватькодированиеиметь Пользовательский элемент Подниматьилиструктураключиз Словарь Можетспособный遇приезжатьизвопрос。
проходитьдля Пользовательский элемент Подниматьилиструктура Реальностьсейчас CodingKeyRepresentable
может гарантировать, что эти ключи обрабатываются правильно во время шифрования и декодирования протокола. Это делает JSON из вывода легче понять исуществовать Swift снаружииспользовать。
Например,из кода ниже,Swift Умеет правильно выполнить:
import Foundation
enum OldSettings: String, Codable {
case name
case twitter
}
let oldDict: [OldSettings: String] = [.name: "Paul", .twitter: "@twostraws"]
let oldData = try JSONEncoder().encode(oldDict)
print(String(decoding: oldData, as: UTF8.self))
Да Распечататьда["twitter","@twostraws","name","Paul"]
Явно не соответствует ожидаемому.
ДаиспользоватьCodingKeyRepresentable
протоколназад。
enum NewSettings: String, Codable, CodingKeyRepresentable {
case name
case twitter
}
let newDict: [NewSettings: String] = [.name: "Paul", .twitter: "@twostraws"]
let newData = try! JSONEncoder().encode(newDict)
print(String(decoding: newData, as: UTF8.self))
Распечатать Сразусоответствоватьожидал Понятно:{"twitter":"@twostraws","name":"Paul”}
https://stackoverflow.com/questions/68466494/what-is-a-swift-un-keyed-container
#unavailable
Условное суждениеSE-0290 представлять Понятноодининдивидуальныйимядля #unavailable
из Новая директива, она связана с #available
Вместо этого некоторый код выполняется в случае сбоя проверки доступности. Это полезно в ситуациях, когда вы просто хотите запустить код, когда конкретная операционная система недоступна.
ииспользовать #available пустой true По сравнению с блоком, сейчас существование можно более лаконично использовать. #unavailable инструкция:
if #unavailable(iOS 15) {
// делать iOS 14 Предыдущая версия нормальный код работыиз
}
Раньше вам приходилось использовать метод else пустого блока:
if #available(iOS 15, *) { } else {
// Code to make iOS 14 and earlier work correctly
}
нуждаться Уведомлениеизда,и #available
Неттакой же,#unavailable
Нетпозволятьиспользовать Подстановочный знак платформы *,Потому что длясуществовать в данном случае,Это может привести к двусмысленности.
Swift 5.5 добавить в Можетмногооодновременно Функция,И 5.6 продолжает улучшать эти функции.,делатьэтоих更Безопасность、Более последовательный, а также для выхода в Swift 6 большего размера.、Будьте готовы к более революционным изменениям.
Максимальное изменение да SE-0337,цельсуществоватьдлянасизкодпоставлять Реальностьсейчас完Полный严格из Проверка параллелизмаиздорожная карта。этотда Инкрементальныйиз:ты Можетиспользовать @preconcurrency
Импортируйте весь индивидуальный модуль и скажите Swift Модуль дасуществовать создан без учёта современных одновременно ситуаций, вы можете @preconcurrency
отметкадляодининдивидуальныйдобрый、структура、свойство、методждать,Чтобы быть более избирательным, используйте.
Другойодининдивидуальныйтолькосуществоватьизменятьизполеда actor изиспользовать,потому чтодляпотому что SE-0327 из результатов, Swift 5.6 Теперь существуют будут выдавать предупреждение, если вы попытаетесь использовать @StateObject Реальность例化одининдивидуальный @MainActor свойства, например:
import SwiftUI
@MainActor class Settings: ObservableObject { }
struct OldContentView: View {
@StateObject private var settings = Settings()
var body: some View {
Text("Hello, world!")
}
}
этотиндивидуальныйпредупреждать Волясуществовать Swift 6 серединаобновлениедляошибка,Так что вы должны быть готовы отказаться от этого кода,改использоватьэтотиндивидуальный
struct NewContentView: View {
@StateObject private var settings: Settings
init() {
_settings = StateObject(wrappedValue: Settings())
}
var body: some View {
Text("Hello, world!")
}
}
Swift 5.6 Содержит серию Swift Package Manager из Улучшений, эти улучшения объединены добавить виспользовать Внешние инструменты сборкиизплагинподдерживатьиз Предварительный Функция。
ссылка