Swift очень лёгок для начала изучения и работы, и именно это один из убедительных аспектов данного языка. Но, чем чаще вы используете этот язык, тем больше вступаете в контакт с более сложными конструкциями.

В Swift 2 вы наверняка сталкивались с @noescape атрибутом, вы понимали что оно значит? В Swift 3, @noescape удалён из языка. Почему? И почему  Swift 3 ввел атрибут @escaping? Постараемся ответить на эти вопросы. 

Каково назначение @noescape?

Даже если  @noescape и удален в Swift 3, очень полезно понять что он значит. Почему? Ответ прост. @noescape атрибут применяется по-умолчанию в Swift 3. Давайте начнем сначала.

Что такое Escaping в замыканиях?

Для начала вы должны понимать что такое «escaping» в замыканиях. Определение очень простое и легкое для понимания. Если замыкание передается как аргумент в функцию, и возвращается при возврате функции, замыкание — выходящее. Это говорит о том, что аргумент замыкания выходит из тела функции. С пониманием этого определения, название термина «escaping» замыкания становится ясным. Верно же?

Если замыкание передается как аргумент в функцию, и возвращается при возврате функции, замыкание — выходящее.

В Swift 2, вы могли пометить параметр функции как @noescape, говоря этим самым компилятору, что замыкание передается в функцию и не будет возвращено из тела функции. Взгляните на следующий пример. Обратите внимание, что этот пример написан на Swift 2.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(completion: (Bool, NSError?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes, completion: completion)
    }

}

Как вы можете заметить requestAuthorization(_:) функция принимает один параметр, замыкание. В Swift 2, замыкания могут по-умолчанию возвращаться из тела функций, поэтому данный пример не выдает никаких ошибок компилятора. Обратите внимание, что completionparameter передается как аргумент в функцию requestAuthorizationToShareTypes(_:readTypes:completion:) метода HKHealthStore. Этот метод асинхронный, это значит, что замыкание вернется после возврата в  requestAuthorization(_:). Другими словами, замыкание, которое мы передаем в requestAuthorization(_:) возвращаемое. Оно возвращается из тела функции requestAuthorization(_:).

Становится интересно, когда мы добавляем @noescape атрибут для параметра функции requestAuthorization(_:). Это пример как раз с  @noescape атрибутом.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(@noescape completion: (Bool, NSError?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorizationToShareTypes(shareTypes, readTypes: readTypes, completion: completion)
    }

}

Мы явно указали компилятору, что параметру «completion» не должно быть позволено, чтобы оно вернулось из тела функции. Результатом получаем, что компилятор выдает ошибку, объясняя, что это неправильно.

The @noescape Attribute in Swift 2

Swift 3

Позвольте мне показать вам, как приведенный выше пример выглядит в Swift 3. Это покажет вам, что изменилось и что вам нужно знать о возвращаемых замыканиях в Swift 3.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(@noescape completion: (Bool, Error?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
    }
    
}

Компилятор сразу уведомлет нас, что @noescape атрибут по-умолчанию Swift 3 и предлагает удалить @noescape. По факту, @noescape больше не используется в Swift 3.

The @noescape attribute is deprecated in Swift 3.

Но так как замыкание, которое мы передаем в requestAuthorization(completion:) (заметьте что объявление методов другое в Swift 3) возвращаемое, мы должны явно пометить параметр как возвращаемый. И должны использовать новый атрибут, @escaping. Это директива SE–0103, a Swift эволюция предложение о том, как сделать не возвращаемые замыкания по умолчанию. И это очень долгожданное изменение, если вы спросите меня.

import HealthKit

class HealthKitManager: NSObject {

    private let healthStore = HKHealthStore()

    func requestAuthorization(completion: @escaping (Bool, Error?) -> Void) {
        var shareTypes = Set<HKSampleType>()
        var readTypes = Set<HKSampleType>()

        // Add Workout Type
        shareTypes.insert(HKSampleType.workoutType())
        readTypes.insert(HKSampleType.workoutType())

        // Request Authorization
        healthStore.requestAuthorization(toShare: shareTypes, read: readTypes, completion: completion)
    }
    
}
We need to mark the closure as escaping with the @escaping attribute to satisfy the compiler.

Так же вы могли заметить что @escaping атрибудт предшевствует типу параметра,а не имени. Это тоже нововведение в Swift 3.

Что означает @escaping?

Это подводит нас к смыслу атрибута @escaping. Так как замыкания, по умолчанию не возвращаемые в Swift 3, возвращаемые замыкания должны быть помечены как таковые. И атрибут @escaping позволяет нам сделать это.

Ошибка компилятора исчезает путем указания замыкания как возвращаемое, используя атрибут @escaping.

Почему это так важно?

Есть несколько преимуществ, чтобы сделать замыкания, не возвращаемые по умолчанию. Наиболее очевидные преимущества производительности и способности для компилятора оптимизировать свой код. Если компилятор знает, что замыкание не является возвращаемым, он может позаботиться о многих из мельчайших деталей управления памятью.

Это также означает, что вы можете использовать self само без проблем, так как замыкание вызывается до завершения функции. Также нет необходимости использовать слабую ссылку на self в замыкании. Это очень отлично!

Вольный перевод статьи — https://cocoacasts.com/what-do-escaping-and-noescaping-mean-in-swift-3/
Что делают @escaping и @noescape в Swift 3?