Скрытие закрытий в Swift

Я новичок в Swift, и я читал руководство, когда сталкивался с закрытием. Я не получил описания руководства вообще. Может кто-нибудь, пожалуйста, объясните мне, что ускользает от закрытия в Swift в простых выражениях.

Рассмотрим этот class:

 class A { var closure: (() -> Void)? func someMethod(closure: () -> Void) { self.closure = closure } } 

someMethod назначает замыкание, переданное в, в свойство classа.

Теперь вот еще один class:

 class B { var number = 0 var a: A = A() func anotherMethod() { a.someMethod { self.number = 10 } } } 

Если я вызову anotherMethod , замыкание { self.number = 10 } будет сохранено в экземпляре A Так как self захвачен в закрытии, экземпляр A также будет содержать сильную ссылку на него.

Это в основном пример скрытого закрытия!

Вы, наверное, задаетесь вопросом: «Что? Так откуда же укрылся оттуда и до?»

Закрытие выходит из области действия метода, в область действия classа. И это можно назвать позже, даже в другом streamе! Это может вызвать проблемы, если не обрабатываться должным образом.

Чтобы избежать случайного выхода из закрытий и возникновения циклов сохранения и других проблем, используйте атрибут @noescape :

 class A { var closure: (() -> Void)? func someMethod(@noescape closure: () -> Void) { } } 

Теперь, если вы попытаетесь написать self.closure = closure , оно не скомпилируется!

Обновить:

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

 class A { var closure: (() -> Void)? func someMethod(closure: @escaping () -> Void) { } } 

Я делаю более простой способ.

Рассмотрим этот пример:

 func testFunctionWithNonescapingClosure(closure:() -> Void) { closure() } 

Вышеупомянутое является закрывающим закрытием, потому что замыкание вызывается перед возвратом метода.

Рассмотрим тот же пример с асинхронной операцией:

 func testFunctionWithEscapingClosure(closure:@escaping () -> Void) { DispatchQueue.main.async { closure() } } 

В приведенном выше примере содержится закрывающее закрытие, потому что вызов закрытия может произойти после возвращения функции из-за асинхронной операции.

  var completionHandlers: [() -> Void] = [] func testFunctionWithEscapingClosure(closure: @escaping () -> Void) { completionHandlers.append(closure) } 

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

Для оптимизации компилятора в Swift 3 были добавлены nonescaping закрытия. Вы можете искать преимущества закрытия nonescaping .

Я считаю, что этот сайт очень полезен в этом вопросе. Простое объяснение:

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

Читайте больше по ссылке, которую я прошел выше! 🙂

Swift 4.1

Из справочника по языку: атрибуты языка Swift Programming (Swift 4.1)

Apple объясняет, что атрибут явно escaping .

Примените этот атрибут к типу параметра в объявлении метода или функции, чтобы указать, что значение параметра может быть сохранено для последующего выполнения. Это означает, что значение позволяет пережить время жизни вызова. Параметры типа функции с атрибутом типа escaping требуют явного использования self. для свойств или методов. Пример использования атрибута экранирования см. В разделе « Закрытие закрытий»

 var completionHandlers: [() -> Void] = [] func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { completionHandlers.append(completionHandler) } 

Функция someFunctionWithEscapingClosure(_:) принимает замыкание как свой аргумент и добавляет его в массив, объявленный вне функции. Если вы не отметили параметр этой функции с помощью @escaping , вы получите ошибку времени компиляции.

Говорят, что замыкание выходит из функции, когда замыкание передается как аргумент функции, но вызывается после возвращения функции. Когда вы объявляете функцию, которая принимает замыкание как один из ее параметров, вы можете написать @escaping перед типом параметра, чтобы указать, что закрытие разрешено.

  • Справочник по методу экземпляра и lambda-параметры
  • Давайте будем гением компьютера.