2017-02-13 4 views
8

У меня есть следующие функции, где у меня есть обработчик завершения, но я получаю эту ошибку:Swift 3: использование Закрытия не-отводящий параметр может позволить ему избежать

Closure use of non-escaping parameter may allow it to escape 

Вот мой код:

func makeRequestcompletion(completion:(_ response:Data, _ error:NSError)->Void) { 
    let urlString = URL(string: "http://someUrl.com") 
    if let url = urlString { 
     let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in 
      completion(data, error) // <-- here is I'm getting the error 
     }) 
    task.resume() 
    } 
} 

enter image description here Любой из вас знает, почему я получаю эту ошибку?

Я действительно ценю Вас помочь

+0

См. Это [Закрытие использования параметра без экранирования может позволить ему уйти] (https://stackoverflow.com/q/38990882/6521116) –

ответ

8

Похоже, вам необходимо явно определить, что закрытие разрешается бежать.

С Apple Developer docs,

A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write @escaping before the parameter’s type to indicate that the closure is allowed to escape.

TLDR; Добавьте @escaping ключевое слово после переменного завершения:

func makeRequestcompletion(completion: @escaping (_ response:Data, _ error:NSError)->Void) { 
    let urlString = URL(string: "http://someUrl.com") 
    if let url = urlString { 
     let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in 
      completion(data, error) // <-- here is I'm getting the error 
     }) 
     task.resume() 
    } 
} 
+0

Почему запрашивается @escaping? – user2924482

+3

Теперь в Swift 3 вы должны явно определить, когда функция содержит обработчик завершения, который выполняется после того, как функция была вызвана (экранирование). Apple говорит: «Функция возвращается после запуска операции, но закрытие не вызывается до завершения операции - закрытие должно быть выполнено, чтобы быть вызванным позже ... Если вы не отметили параметр этого функции с @escaping, вы получите ошибку времени компиляции ». –

+0

Просто опустился, чтобы поблагодарить @MarkBarrasso за его объяснение. Прочитал, но не понял всю «убегающую» вещь, и этот комментарий заставил меня увидеть свет;) –

3

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

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

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

+0

пример кода, как исправить было бы здорово. – Linasses

+0

@Linasses 'завершение: @escaping (_ response: Data, _ error: NSError) -> Void'. – zneak

+1

Ошибка '@ escaping' всплывает, хотя у меня уже есть' @ escaping': | open static func resumeSession (_ completion: @escaping (Результат ) -> (Void)) {@ escaping error всплывает, хотя у меня уже есть @ escaping: | open static func resumeSession (_ completion: @ escaping (Result ) -> (Void)) { –

0

@escaping является заразительным для всех вызывающих методов, и компилятор определяет, когда вы должен включить его.

Рассмотрим следующий пример (который компилирует):

dispatchSometime({ print("Oh yeah") }) 

func dispatchSometime(_ block:()->()) { 
    dispatchNow(block) 
} 

func dispatchNow(_ block:()->()) { 
    block() 
} 

Этот модифицированный пример, однако, производит два ошибки типа non-escaping parameter may allow it to escape:

dispatchSometime({ print("Oh yeah") }) 

func dispatchSometime(_ block:()->()) { 
    dispatchLater(block) 
} 

func dispatchLater(_ block:()->()) { 
    DispatchQueue.main.async(execute: block) 
} 

Отправка на главной означает потребности dispatchLater метод @escaping, и как только вы добавили это, метод dispatchSometimeтакже требует: @escaping для примера для компиляции.

dispatchSometime({ print("Oh yeah") }) 

func dispatchSometime(_ block: @escaping()->()) { 
    dispatchLater(block) 
} 

func dispatchLater(_ block: @escaping()->()) { 
    DispatchQueue.main.async(execute: block) 
} 

Однако отнимать просто:

  • Продолжайте добавлять @escaping вверх по цепочке вызовов, пока компилятор не перестанет жаловаться.
  • Ключевое слово ничего не меняет: это предупреждение, которое говорит, по существу, «будьте осторожны с использованием weak с захваченными переменными, поскольку они могут сохраняться вместе с самим блоком».

Последствия

Действительно весело случае это, где вы должны настроить несколько способов включить @escaping ключевое слово, которое получает компилятор прекратить жаловаться. Однако, если эти методы фактически соответствуют протоколу, методы этого протокола должны также получить ключевое слово @escaping, которое также заражает все другие протокольные согласования. Весело!

Смежные вопросы