Skip to content

Latest commit

 

History

History
255 lines (198 loc) · 8.45 KB

File metadata and controls

255 lines (198 loc) · 8.45 KB

Closure

  1. ✔️ Синтаксис замыкания
  2. ✔️ 100 Days of SwiftUI. Day 6: closures part one
  3. ✔️ 100 Days of SwiftUI. Day 7: closures part two

1. Вызов Замыкания

при вызове 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

2. Trailing closure (последующее замыкание)

Если последним параметром функции является замыкание, 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
}()

3. Trailing замыкание с параметром

  1. ✔️ Shorthand parameter names
  2. ✔️ When should you use shorthand parameter names?

Можно явно указывать принимаемые параметры: (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"
}

4. Функция, возвращающая замыкание

  1. ✔️ Returning closures from functions
  2. ✔️ Optional: Returning closures from functions
  3. ✔️ 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

  1. ✔️ Capturing values
  2. ✔️ Why do Swift’s closures capture values?
  3. ✔️ Closures Capture Semantics: Catch them all!
  4. ✔️ 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

5. retain cycle

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

6.

Закрытия - это обычные типы, поэтому их можно помещать в массивы, словари и т.д. Когда это происходит, они хранятся в куче (т.е. являются справочными типами). Они захватывают окружающие их переменные, если у меня есть замыкание, то это просто функция, встроенная прямо в мой код. Это может создать цикл памяти, потому что можно перехватить класс, в котором находится массив операций. Если вы это сделаете, то это замыкание перехватит этот класс в кучу. И этот класс через свой массив перехватил замыкание в куче. Они указывают друг на друга через массив. That closure is keeping the class, the class is keeping the closure.

6. Capture List

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
  1. Ситуация другая с классами:
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