- ✔️ Синтаксис замыкания
- ✔️ 100 Days of SwiftUI. Day 6: closures part one
- ✔️ 100 Days of SwiftUI. Day 7: closures part two
при вызове closure мы не пишем название параметра:
var buyMagazine = { (name: String) -> Int in let amount = 10 print("\(name) costs \(amount)") return amount } buyMagazine(name: "Wired") // Extraneous argument label 'name:' in call Правильно: buyMagazine("Wired") // Wired costs 10
Если последним параметром функции является замыкание, Swift позволяет использовать специальный синтаксис, называемый синтаксисом trailing closure. Вместо того, чтобы передавать ваше замыкание в качестве параметра, вы передаете его непосредственно после функции в фигурных скобках.
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
travel() {
print("I'm driving in my car")
}
Поскольку других параметров нет, мы можем полностью исключить круглые скобки:
travel {
print("I'm driving in my car")
}
Без trailing closure:
animate(duration: 3, animations: {
print("Fade out the image")
})
С trailing closure:
animate(duration: 3) {
print("Fade out the image")
}
Do not forget the trailing () to execute the closure
let button: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont.systemFont(ofSize: ViewMetrics.fontSize)
...
return button
}()
Можно явно указывать принимаемые параметры: (grade: Int) -> Stirng in return
func scoreToGrade(score: Int, gradeMapping: (Int) -> String) {
print("That's a \(gradeMapping(score)).")
}
scoreToGrade(score: 89) { (grade: Int) -> String in
return "You scored \(grade) points"
}
Можно просто указать название принимаемого параметра: grade in
func scoreToGrade(score: Int, gradeMapping: (Int) -> String) {
print("Wow! \(gradeMapping(score)).")
}
scoreToGrade(score: 89) { grade in
"You scored \(grade) points"
}
Более того, Swift имеет более сокращенный синтаксис. Вместо того, чтобы писать, grade in мы можем позволить Swift автоматически указывать имена для параметров закрытия. Они вызываются со знаком доллара, а затем с числа, отсчитываемого от 0.
func scoreToGrade(score: Int, gradeMapping: (Int) -> String) {
print("Wow! \(gradeMapping(score)).")
}
scoreToGrade(score: 89) {
"You scored \($0) points"
}
- ✔️ Returning closures from functions
- ✔️ Optional: Returning closures from functions
- ✔️ Test (4, 5, 11): Returning closures from functions
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
let result = travel()
result("London")
- ✔️ Capturing values
- ✔️ Why do Swift’s closures capture values?
- ✔️ Closures Capture Semantics: Catch them all!
- ✔️ Text: Capturing values
func travel() -> (String) -> Void {
var counter = 1
return {
print("\(counter). I'm going to \($0)")
counter += 1
}
}
let result = travel()
result("London") // 1. I'm going to London
result("London") // 2. I'm going to London
result("London") // 3. I'm going to London
C замыканиями нужно быть осторожным, они создают retain cycle:
class TeamMember {
var closure: (() -> ())?
func method(closure: () -> ()?) {
closure()
}
func describeYourself() {
print(“I’m!“)
}
func describeOtherself() {
print(“I’!“)
}
deinit {
print(“Deinit”)
}
}
var member: TeamMember? = TeamMember()
//ЗДЕСЬ СОЗДАЕТСЯ retain cycle
member!.closure = { [weak member] in
member?.describeYourself()
}
member!.closure?()
//А ЗДЕСЬ - НЕТ
member!.method(closure: {
member!.describeOtherself()
})
//member!.closure = nil
//member!.method(closure: {nil})
member = nil
Закрытия - это обычные типы, поэтому их можно помещать в массивы, словари и т.д. Когда это происходит, они хранятся в куче (т.е. являются справочными типами). Они захватывают окружающие их переменные, если у меня есть замыкание, то это просто функция, встроенная прямо в мой код. Это может создать цикл памяти, потому что можно перехватить класс, в котором находится массив операций. Если вы это сделаете, то это замыкание перехватит этот класс в кучу. И этот класс через свой массив перехватил замыкание в куче. Они указывают друг на друга через массив. That closure is keeping the class, the class is keeping the closure.
struct Cars {
var name = "Tesla"
}
var namedCars = Cars()
let closure = { [namedCars] in
print("I love \(namedCars.name)")
}
namedCars.name = "mercedes"
closure()
Будет напечатано: I love Tesla. Список захвата создаст копию переменной в момент объявления замыкания. Это означает, что захваченная переменная не изменит своего значения, даже после присвоения нового значения.
Если вы опустите список захвата в замыкании, то компилятор будет использовать ссылку, а не копию. Вызов замыкания отразит изменение в переменной:
struct Cars {
var name = "Tesla"
}
var namedCars = Cars()
let closure = { in
print("I love \(namedCars.name)")
}
namedCars.name = "mercedes"
closure() // I love mercedes
- Ситуация другая с классами:
class Cars {
var name = "Tesla"
}
var namedCars = Cars()
let closure1 = { [namedCars] in
print("I love \(namedCars.name)")
}
let closure2 = {
print("I love \(namedCars.name)")
}
namedCars.name = "mercedes"
closure1() // I love mercedes
closure2() // I love mercedes