- PVSM.RU - https://www.pvsm.ru -
Оглавление
SE-0172 [13] добавляет новый RangeExpression
протокол и набор префиксных/постфиксных операторов для определения односторонних диапазонов, то есть диапазоны, в которых либо нижняя, либо верхняя граница не определена
Можно использовать одностороннюю последовательность, чтобы создать бесконечную последовательность, то есть более гибкая замена enumerated()
, когда не хочется, чтобы нумерация начиналась с нуля:
let letters = ["a","b","c","d"]
let numberedLetters = zip(1..., letters)
Array(numberedLetters)
Когда односторонняя последовательность используется в сабскрипте коллекции, то startIndex
или endIndex
“самозаполняют” в коллекции пропущенную верхнюю или нижнюю границу, соответсвенно.
let numbers = [1,2,3,4,5,6,7,8,9,10]
numbers[5...] // вместо numbers[5..<numbers.endIndex]
Это когда односторонняя последовательность используется в конструкции сравнения паттернов, например в case
или switch
. Обратите внимание, что компилятор пока не может определить, что switch
является здесь лишним.
let value = 5
switch value {
case 1...:
print("greater than zero")
case 0:
print("zero")
case ..<0:
print("less than zero")
default:
fatalError("unreachable")
}
SE-0168 [14] вводит простой синтаксис для многострочных строковых литералов ("""
). В многострочном литерале не нужно экранировать одинарные кавычки, что означает, что такие форматы как JSON и HTML могут быть вставлены в них безо всякого экранирования. Отбивка закрывающего литерала определяет сколько пробелов будет удалено с начала каждой строки.
let multilineString = """
This is a multi-line string.
You don't have to escape "quotes" in here.
The position of the closing delimiter
controls whitespace stripping.
"""
print(multilineString)
Чтобы увидеть результат работы print
можно вывести консоль нажав (View > Debug Area > Activate Console).
SE-0163 [15] является первой частью пересмотренной строковой модели для Swift 4. Самое большое изменение, что теперь строка — это коллекция (как раньше было в Swift 1.x), то есть функциональность String.CharacterView
была свернута в родительский тип. (Другие виды, UnicodeScalarView
, UTF8View
, и UTF16View
, по прежнему присутствуют.)
Обратите внимание, что SE-0163 еще не полностью реализован, и в будущем будут более строгие изменения.
let greeting = "Hello, !"
// теперь не нужно опускаться до .characters
greeting.count
for char in greeting {
print(char)
}
Экземпляры слайса строки теперь являются типом Substring
. Оба типа String
и Substring
реализуют протокол StringProtocol
. Почти все API для строк живет в StringProtocol
поэтому String
и StringProtocol
в основном ведут себя одинаково.
let comma = greeting.index(of: ",")!
let substring = greeting[..<comma]
type(of: substring)
// API от String можно использовать в Substring
print(substring.uppercased())
Swift 4 будет поддерживать Unicode 9, исправлены проблемы с надлежащей кластеризацией графем для современных эмодзи. Всё указанное ниже теперь являются одним символом:
"".count // person + skin tone
"".count // family with four members
"u{200D}u{200D}u{200D}".count // family + skin tones
"".count // person + skin tone + profession
Хабрапарсер сожрал все эмодзи, с ними смотреть тут [16]
Character.unicodeScalars
Теперь можно получить доступ к точкам Character
напрямую без превращения их в строку (SE-0178 [17]).
let c: Character = ""
Array(c.unicodeScalars)
SE-0169 [18] изменяет правила контроля доступа так, что теперь приватные объявления видимы в экстеншенах родительского типа в том же файле. Это позволяет разбить определение вашего типа на несколько экстеншенов и по-прежнему использовать приватный доступ для большинства «приватных» вещей, уменьшая потребность в использовании ключа доступа fileprivate
.
struct SortedArray<Element: Comparable> {
private var storage: [Element] = []
init(unsorted: [Element]) {
storage = unsorted.sorted()
}
}
extension SortedArray {
mutating func insert(_ element: Element) {
// storage тут доступен
storage.append(element)
storage.sort()
}
}
let array = SortedArray(unsorted: [3,1,2])
// storage _не_ доступен тут (в отличии от fileprivate)
//array.storage // error: 'storage' is inaccessible due to 'private' protection level
Вероятно, одна из главных особенностей Swift 4 это новая модель ключей пути (key path) описанная в SE-0161 [19]. В отличии от строковых ключей пути в Cocoa, в Swift ключи пути строго типизированные.
struct Person {
var name: String
}
struct Book {
var title: String
var authors: [Person]
var primaryAuthor: Person {
return authors.first!
}
}
let abelson = Person(name: "Harold Abelson")
let sussman = Person(name: "Gerald Jay Sussman")
let sicp = Book(title: "Structure and Interpretation of Computer Programs", authors: [abelson, sussman])
Ключи пути можно указывать, начиная с корневого типа и опускаться до любой комбинации свойств и имен.
Написание ключа пути начинается с бэкслеша: Book.title
. Любой тип в Swift принимает [keyPath: …]
— сабскрипт для получения или установки значения для нужного ключа пути.
sicp[keyPath: Book.title]
// Ключи пути могут работать с вычисляемыми свойствами
sicp[keyPath: Book.primaryAuthor.name]
Ключи пути — это объект KeyPath
, который можно хранить и производить манипуляции с ним. Например, можно добавить дополнительные сегменты к ключу пути, чтоб углубиться дальше.
let authorKeyPath = Book.primaryAuthor
type(of: authorKeyPath)
let nameKeyPath = authorKeyPath.appending(path: .name) // можно опустить тип имени если компилятор может его вычислить
sicp[keyPath: nameKeyPath]
В ключах пути так же можно использовать сабскрипт нотацию. Это делает их очень удобными для работы с коллекциям, массивам или словарям. Эта функциональность пока еще не реализована в текущем снэпшоте.
//sicp[keyPath: Book.authors[0].name]
// INTERNAL ERROR: feature not implemented: non-property key path component
SE-0166: Swift Archival & Serialization [20] определяет как типы в Swift (классы, структуры, и енумы) будут сериализовывать и архивировать себя. Типы могут сделать себя (раз-)архивируемыми реализовав протокол Codable
.
В большинстве случаев имплементация Codable
протокола — все что требуется, компилятор может сгенерировать остальную часть имплементации сам, только если все члены типа реализуют Codable
. Так же можно переопределить стандартное поведение, если нужно поменять то как тип себя сериализует. В этой теме есть много нюансов — обязательно ознакомьтесь с предложением для уточнения деталей.
// Делаем свой тип сериализуемым (и всех его членов) унаследовав протокол Codable
struct Card: Codable {
enum Suit: String, Codable {
case clubs, spades, hearts, diamonds
}
enum Rank: Int, Codable {
case ace = 1, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king
}
var suit: Suit
var rank: Rank
}
let hand = [Card(suit: .clubs, rank: .ace), Card(suit: .hearts, rank: .queen)]
Когда у вас есть значение реализующее Codable
, нужно передать его кодировщику, чтобы заархивировать.
Вы можете написать свои кодеры и декодеры, которые используют инфраструктуру от Codable
, но в Swift будут поставляться встроенные для JSON (JSONEncoder
и JSONDecoder
) и для списка свойств (PropertyListEncoder
и PropertyListDecoder
). Они определены в SE-0167 [21]. NSKeyedArchiver
так же будет поддерживать все Codable
типы
import Foundation
var encoder = JSONEncoder()
// Свойства предоставляемые JSONEncoder для кастомизации вывода
encoder.dataEncodingStrategy
encoder.dateEncodingStrategy
encoder.nonConformingFloatEncodingStrategy
encoder.outputFormatting
encoder.userInfo
let jsonData = try encoder.encode(hand)
String(data: jsonData, encoding: .utf8)
let decoder = JSONDecoder()
let decoded = try decoder.decode([Card].self, from: jsonData)
Dictionary
и Set
SE-0165 [22] добавляет несколько улучшений для Dictionary
и Set
.
Создание словаря из последовательности пар ключ-значение.
let names = ["Cagney", "Lacey", "Bensen"]
let dict = Dictionary(uniqueKeysWithValues: zip(1..., names))
dict[2]
Теперь можно определить то, как дубли ключей будут обработаны, когда создается словарь из последовательности или производится слияние последовательности в текущий словарь.
let duplicates = [("a", 1), ("b", 2), ("a", 3), ("b", 4)]
let letters = Dictionary(duplicates, uniquingKeysWith: { (first, _) in first })
letters
let defaults = ["foo": false, "bar": false, "baz": false]
var options = ["foo": true, "bar": false]
// Этот код упадет с ошибкой типизации: error: generic parameter 'S' could not be inferred
// Я надеюсь что это относится к https://bugs.swift.org/browse/SR-922
//options.merge(defaults) { (old, _) in old }
Можно определить значччение по умолчанию для несуществующих ключей, как аргумент сабскрипта, сделав возвращаемый тип не опциональным.
dict[4, default: "(unknown)"] // вернется значение которе не нужно анврапить
Это особенно важно когда нужно мутировать значение через сабскрипт:
let source = "how now brown cow"
var frequencies: [Character: Int] = [:]
for c in source {
frequencies[c, default: 0] += 1
}
frequencies
map
и filter
filter
возвращает Dictionary
а не Array
. Аналогично, новый метод mapValues
преобразует значения c сохранением его структуры
let filtered = dict.filter {
$0.key % 2 == 0
}
type(of: filtered)
let mapped = dict.mapValues { value in
value.uppercased()
}
mapped
Set.filter
так же возвращает Set
а не Array
.
let set: Set = [1,2,3,4,5]
let filteredSet = set.filter { $0 % 2 == 0 }
type(of: filteredSet)
Группировка последовательности значений в букеты. разбиваем слова в списке по их первой букве.
let contacts = ["Julia", "Susan", "John", "Alice", "Alex"]
let grouped = Dictionary(grouping: contacts, by: { $0.first! })
grouped
MutableCollection.swapAt
SE-0173 [23] представляет новый метод для обмена двух элементов в коллекции. В отличии от существующего swap(_:_:)
, метод swapAt(_:_:)
принимает индексы элементов, которые нужно обменять, а не сами элементы (через inout
аргументы).
Причина для добавления этого метода в том, что обмен с двумя inout
аргументами несовместим
с новым правилами доступа к памяти SE-0176 [24]. Существующая функция swap(_:_:)
больше не будет работать для обмена двух элементов одной и той же коллекции.
var numbers = [1,2,3,4,5]
numbers.swapAt(0,1)
// Will be illegal in Swift 4 (not implemented yet)
swap(&numbers[3], &numbers[4])
numbers
reduce
с поддержкой inout
SE-0171 [25] добавляет вариант reduce
метода в котором результат передается как inout
в функцию combine
. Это может быть существенным ускорением для алгоритмов которые используют reduce
чтобы инкрементально строить последовательности, путем исключения копирования и промежуточного результата.
SE-0171 пока что не реализован
// Пока что не работает
extension Sequence where Iterator.Element: Equatable {
func uniq() -> [Iterator.Element] {
return reduce(into: []) { (result: inout [Iterator.Element], element) in
if result.last != element {
result.append(element)
}
}
}
}
[1,1,1,2,3,3,4].uniq()
Как представлено в SE-0148 [26], сабскрипт теперь может принимать и возвращать аргументы в виде генериков.
Канонический пример — это тип который предсталяет JSON
данные: можно определить сабскрипт с генериком, чтобы контекст, вызывающего кода смог определить ожидаемый возвращаемый тип.
struct JSON {
fileprivate var storage: [String:Any]
init(dictionary: [String:Any]) {
self.storage = dictionary
}
subscript<T>(key: String) -> T? {
return storage[key] as? T
}
}
let json = JSON(dictionary: [
"name": "Berlin",
"country": "de",
"population": 3_500_500
])
// Теперь не нужно использовать as? Int
let population: Int? = json["population"]
Другой пример: сабскрипт в Collection
, который принимает последовательность индексов и возвращает массив значений этих индексов.
extension Collection {
subscript<Indices: Sequence>(indices indices: Indices) -> [Iterator.Element] where Indices.Iterator.Element == Index {
var result: [Element] = []
for index in indices {
result.append(self[index])
}
return result
}
}
let words = "Lorem ipsum dolor sit amet".split(separator: " ")
words[indices: [1,2]]
NSNumber
SE-0170 [27] исправляет некоторое опасное поведение с мостом между числовым типом в Swift и NSNumber
.
import Foundation
let n = NSNumber(value: UInt32(543))
let v = n as? Int8 // nil in Swift 4. This would be 31 in Swift 3 (try it!).
Теперь можно писать эквивалент кода на Objective-C UIViewController <SomeProtocol> *
в Swift,
например объявить переменную с конкретным типом и связать её к одному или нескольким протоколам одновременно (SE-0156 [28]). Синтаксис let variable: SomeClass & SomeProtocol1 & SomeProtocol2
import Cocoa
protocol HeaderView {}
class ViewController: NSViewController {
let header: NSView & HeaderView
init(header: NSView & HeaderView) {
self.header = header
super.init(nibName: nil, bundle: nil)!
}
required init(coder decoder: NSCoder) {
fatalError("not implemented")
}
}
// Нельзя передать просто NSView который не реализует протокол
// ViewController(header: NSView())
// error: argument type 'NSView' does not conform to expected type 'NSView & HeaderView'
// Должен пройти как NSView (сабкласс) который так же реализует протокол
extension NSImageView: HeaderView {}
ViewController(header: NSImageView()) // работает
Автор: JiLiZART
Источник [29]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/256635
Ссылки в тексте:
[1] Односторонние диапазоны: #odnostoronnie-diapazony
[2] Строки: #stroki
[3] Приватные объявления видимы в экстеншенах того же файла: #privatnye-obyavleniya-vidimy-v-ekstenshenah-togo-zhe-fayla
[4] Умные ключи пути: #umnye-klyuchi-puti
[5] Архивирование и сериализация: #arhivirovanie-i-serializaciya
[6] Улучшения в Dictionary и Set: #uluchsheniya-v-dictionary-i-set
[7] Метод MutableCollection.swapAt: #metod-mutablecollectionswapat
[8] reduce с поддержкой inout: #reduce-s-podderzhkoy-inout
[9] Генеретики в сабскриптах: #generetiki-v-sabskriptah
[10] Мостик для NSNumber: #mostik-dlya-nsnumber
[11] Экземпляры классов и подтипов: #ekzemplyary-klassov-i-podtipov
[12] Скачать последний снепшот Swift 4 с сайта: https://swift.org/download/#snapshots
[13] SE-0172: https://github.com/apple/swift-evolution/blob/master/proposals/0172-one-sided-ranges.md
[14] SE-0168: https://github.com/apple/swift-evolution/blob/master/proposals/0168-multi-line-string-literals.md
[15] SE-0163: https://github.com/apple/swift-evolution/blob/master/proposals/0163-string-revision-1.md
[16] смотреть тут: https://gist.github.com/JiLiZART/c0bfe8280fc0802ac634c68880e756a8
[17] SE-0178: https://github.com/apple/swift-evolution/blob/master/proposals/0178-character-unicode-view.md
[18] SE-0169: https://github.com/apple/swift-evolution/blob/master/proposals/0169-improve-interaction-between-private-declarations-and-extensions.md
[19] SE-0161: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
[20] SE-0166: Swift Archival & Serialization: https://github.com/apple/swift-evolution/blob/master/proposals/0166-swift-archival-serialization.md
[21] SE-0167: https://github.com/apple/swift-evolution/blob/master/proposals/0167-swift-encoders.md
[22] SE-0165: https://github.com/apple/swift-evolution/blob/master/proposals/0165-dict.md
[23] SE-0173: https://github.com/apple/swift-evolution/blob/master/proposals/0173-swap-indices.md
[24] SE-0176: https://github.com/apple/swift-evolution/blob/master/proposals/0176-enforce-exclusive-access-to-memory.md
[25] SE-0171: https://github.com/apple/swift-evolution/blob/master/proposals/0171-reduce-with-inout.md
[26] SE-0148: https://github.com/apple/swift-evolution/blob/master/proposals/0148-generic-subscripts.md
[27] SE-0170: https://github.com/apple/swift-evolution/blob/master/proposals/0170-nsnumber_bridge.md
[28] SE-0156: https://github.com/apple/swift-evolution/blob/master/proposals/0156-subclass-existentials.md
[29] Источник: https://habrahabr.ru/post/329580/
Нажмите здесь для печати.