Привет Хабр! Язык программирования Swift обладает большой популярностью ввиду его использования в написании приложений под iOS, поэтому его развитие представляет интерес для всех занятых в этой области.
Давайте посмотрим что нового приготовил разработчикам Swift версии 5.3.
Новая версия (SE-0279) предлагает новый синтаксис для конечных замыканий, который позволяет вызывать несколько замыканий в качестве параметров функции более читаемым способом. Это сводит к минимуму использование слишком большого количества скобок в сигнатуре функции.
На примере 2 вариантов вызова видно, что визуально второй смотрится несколько приятнее.
В настоящее время каждый пункт catch в операторе do-catch может содержать только один шаблон. Чтобы обойти эту проблему, разработчики будут использовать возможности вариантов коммутаторов для включения сопоставления шаблонов в тело операторов catch, тем самым увеличивая вложенный и дублированный код (SE-0276).
Catch-пункты теперь позволяют пользователю указывать разделенный запятыми список шаблонов с возможностью связывания переменных с телом catch — как в switch-е.
До сих пор сравнивать 2 элемента перечисления было не то что бы «приятным» делом. Надо было конформить Comparable и написать статическую функцию < для определения того, является ли исходное значение меньше другого.
Теперь Swift (SE-0266) позволяет не «париться» над реализацией всех этих вещей, и не нужно явно реализовывать протокол, пока у вашего перечисления есть одинаковые типы. Если соответствующие значения не заданы, перечисления будут сравниваться по семантическому порядку объявления.
Отныне можно опускать self в тех местах, где это больше не нужно (SE-0269). Ранее использование self в замыканиях было необходимо, когда мы захватывали значения из внешней области видимости.
Новая версия языка (SE-0267) ввела возможность присоединять where к функциям внутри универсальных типов и расширений.
В стек можно добавить новый метод sorted (), но только для тех случаев, когда элементы внутри стека конформят Comparable.
Введена Float16 плавающая точка половинной точности (SE-0277). С появлением машинного обучения на мобильных устройствах в последние годы это только указывает на амбиции Apple в продвижении данной темы. Float 16 обычно используется на мобильных графических процессорах для вычислений и в качестве сжатого формата для весов в приложениях, использующих ML.
Swift 5.3 ввел множество улучшений для Swift Package Manager (SPM). Рассмотрим же их.
Таким образом, можно констатировать, что версия 5.3 привнесла несколько хороших нововведений, которые будут подхватываться со временем разработчиками.
Давайте посмотрим что нового приготовил разработчикам Swift версии 5.3.
Multiple trailing closures
Новая версия (SE-0279) предлагает новый синтаксис для конечных замыканий, который позволяет вызывать несколько замыканий в качестве параметров функции более читаемым способом. Это сводит к минимуму использование слишком большого количества скобок в сигнатуре функции.
import Foundation
func load(url: String, success: (Data) -> Void, failure: (Error) -> Void) {
}
// old version
load(url: "someURL", success: { data in }, failure: { error in })
// new multiple trailing closures
load(url: "someURL") { (data) in
// do smth
} failure: { (error) in
// do smth
}
На примере 2 вариантов вызова видно, что визуально второй смотрится несколько приятнее.
Multi-Pattern Catch Clauses
В настоящее время каждый пункт catch в операторе do-catch может содержать только один шаблон. Чтобы обойти эту проблему, разработчики будут использовать возможности вариантов коммутаторов для включения сопоставления шаблонов в тело операторов catch, тем самым увеличивая вложенный и дублированный код (SE-0276).
Catch-пункты теперь позволяют пользователю указывать разделенный запятыми список шаблонов с возможностью связывания переменных с телом catch — как в switch-е.
enum NetworkError: Error {
case failure, timeout
}
// old version
func networkCall(){
do{
try someNetworkCall()
} catch NetworkError.timeout{
print("timeout")
} catch NetworkError.failure{
print("failure")
}
}
// new multi-Pattern catch clauses
func networkCall(){
do{
try someNetworkCall()
} catch NetworkError.failure, NetworkError.timeout {
print("handle for both")
}
}
Synthesized Comparable Conformance for Enums
До сих пор сравнивать 2 элемента перечисления было не то что бы «приятным» делом. Надо было конформить Comparable и написать статическую функцию < для определения того, является ли исходное значение меньше другого.
Теперь Swift (SE-0266) позволяет не «париться» над реализацией всех этих вещей, и не нужно явно реализовывать протокол, пока у вашего перечисления есть одинаковые типы. Если соответствующие значения не заданы, перечисления будут сравниваться по семантическому порядку объявления.
enum Brightness: Comparable {
case low(Int)
case medium
case high
}
([.high, .low(1), .medium, .low(0)] as [Brightness]).sorted()
// [Brightness.low(0), Brightness.low(1), Brightness.medium, Brightness.high]
количество self-ов можно будет сократить
Отныне можно опускать self в тех местах, где это больше не нужно (SE-0269). Ранее использование self в замыканиях было необходимо, когда мы захватывали значения из внешней области видимости.
struct OldView: View {
var body: some View {
Button(action: {
self.sayHello()
}) {
Text("Press")
}
}
func sayHello() {}
}
struct NewView: View {
var body: some View {
Button {
sayHello()
} label: {
Text("Press")
}
}
func sayHello(){}
}
Where в generic-ах
Новая версия языка (SE-0267) ввела возможность присоединять where к функциям внутри универсальных типов и расширений.
struct Stack<Element> {
private var array = [Element]()
mutating func push(_ obj: Element) {
array.append(obj)
}
mutating func pop(_ obj: Element) -> Element? {
array.popLast()
}
}
extension Stack {
func sorted() -> [Element] where Element: Comparable {
array.sorted()
}
}
В стек можно добавить новый метод sorted (), но только для тех случаев, когда элементы внутри стека конформят Comparable.
Новый тип Float16
Введена Float16 плавающая точка половинной точности (SE-0277). С появлением машинного обучения на мобильных устройствах в последние годы это только указывает на амбиции Apple в продвижении данной темы. Float 16 обычно используется на мобильных графических процессорах для вычислений и в качестве сжатого формата для весов в приложениях, использующих ML.
let f16: Float16 = 7.29
Swift 5.3 ввел множество улучшений для Swift Package Manager (SPM). Рассмотрим же их.
- SE-0271 (Package Manager Resources) позволяет SPM содержать такие ресурсы, как изображения, аудио, JSON и многое другое.
- SE-0272 (Package Manager Binary Dependencies) позволяет SPM использовать бинарные пакеты наряду с существующей поддержкой исходных пакетов. Это означает, что общие SDK с закрытым исходным кодом, такие как Firebase, теперь могут быть интегрированы с помощью SPM.
- SE-0273 (Package Manager Conditional Target Dependencies) позволяет настроить целевые объекты так, чтобы они имели зависимости только для определенных платформ и конфигураций. Например, можно обозначить специальные дополнительные фреймворки при компиляции для Linux.
- SE-0278 позволяет добавлять локализованные ресурсы.
Таким образом, можно констатировать, что версия 5.3 привнесла несколько хороших нововведений, которые будут подхватываться со временем разработчиками.
DmitriyDev
А вот то что разрешили не использовать self в замыканиях мне не очень нравится, так как большинство утечек памяти как раз было связано с тем что люди забывают про то что self захватывается замыканием. Найти проблему было достаточно легко, просто через поиск «.self», сейчас же это будет чуть-чуть сложнее, так как «sayHello()» может быть и другим замыканием, которое было объявлено выше. Но посмотрим как оно пойдёт, может я не прав и это будет действительно лучше.
XanKraegor
К счастью, сделано по уму: исключение сделано для типов значений, которые вовсе не передаются по ссылке, и если есть явное указание на тип self в замыкании, например ‘execute { [self] in’. И да, я тоже запускаю поиск по ‘self.’ для поиска утечек :)
IgorFedorchuk
как это помогает? достаточно давно можно писать без скобок
guard let self = self else { return }
XanKraegor
При self из числа value type можно не писать
guard let self
вообще, а в некоторых случаях, когда объект и так висит в памяти от начала до конца (сразу приходят в голову только синглтоны), можно просто указать[self] in
вначале тела замыкания, и тоже сэкономить 5 символов в каждой строке. Как-то так :)IgorFedorchuk
если висит в памяти от начала и до конца как при UIView.animate, то зачем указывать
?XanKraegor
Наверно, чтобы не заставляло дальше по тексту писать self.methodName или self.varName, — ну любят люди экономить по 5 букв, не знаю, что ещё сказать. Главное для меня то, что не убрали вообще self в замыканиях, так как это стало бы источником дополнительных ошибок.
Висят в памяти от начала и до конца работы всего приложения. Замыкание
animations: @escaping () -> Void
наоборот не должно иметь в себе [self] in, если вызов происходит из самого View, так как это будет подвешивать его в памяти (View держит ссылку на@escaping
замыкание, а замыкание — на View. В таком случае как раз нужен [weak self] in и всё дальше как обычно.IgorFedorchuk
насколько понимаю, будет подвешивать в памяти в любом случае, так как анимация происходит на главном потоке и вызов completion гарантируется?
XanKraegor
Немного не понял вопрос, что значит гарантируется?
В нормальной ситуации (без ошибочной ссылки из стороннего объекта на замыкание и без reatin cycle, когда замыкание и View удерживают друг друга)
@escaping
такая ситуация встречаться не должна.Выполнение
@escaping
возможно, пока жив в памяти соответствующий экземпляр замыкания. Если не сохранять ссылку на замыкание специально, то на него держит сильную ссылку порождающий экземпляр класса (в данном случае View), а также может держать держать ссылку экземпляр CFRunLoopObserver (если не ошибаюсь) главного потока, в котором код выполняется асинхронно или другого потока, хотя анимация на другом потоке выполняться не должна вообще.Если экземпляр View уничтожается и ссылка на замыкание не хранится где-то ещё, то runloop:
1) дойдет до обработки события счётчика ссылок и обнаружит, что количество ссылок на замыкание = 0, после чего объявит использовавшуюся под него память свободной. Продолжения расчётов для анимации не произойдет;
2) runloop на главном потоке увидит наличие удерживающей ссылки от другого потока на замыкание. В этом случае я затрудняюсь ответить, что происходит. Но думаю, что скорее всего сторонний поток завершит выполнение (уже ненужной анимации, потому её и нужно исполнять на главном!), перейдет обратно по стеку, отпустит последнюю ссылку на замыкание. После чего замыкание также будет уничтожено (а возможно и весь поток, если после этого у него пустует стек и нечего больше выполнять).
candyapple
Тут скорее про такое поведение в структурах, ибо там это не опасно.