Нельзя ссылаться на локальную функцию с захватом из другой локальной функции (быстро)
var first_name = ""
func problemFunc() {
FBRequestConnection.startForMeWithCompletionHandler { (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if let fbGraphUserDict = result as? Dictionary<String, AnyObject>{
first_name = fbGraphUserDict["first_name"] as NSString
println(first_name)
}
}
}
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
NSLog("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
NSLog("User signed up and logged in through Facebook!")
} else {
NSLog("User logged in through Facebook!")
problemFunc() // error is here
}
})
Этот код находится внутри кнопки @Ibaction. Я не могу построить, потому что вызов функции problemFunc() вызывает сообщение об ошибке в заголовке этого сообщения. Если я переведу определение first_name var внутри проблемыFunc, он будет работать нормально. Но мне это нужно, потому что другой функции нужно будет получить доступ к ее значению.
Я действительно не уверен, что вызывает эту проблему, если у вас есть ключ, пожалуйста, помогите.
Ответы
Ответ 1
Используйте закрытие вместо функции:
var first_name = ""
let problemFunc = { () -> () in
FBRequestConnection.startForMeWithCompletionHandler { (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if let fbGraphUserDict = result as? Dictionary<String, AnyObject>{
first_name = fbGraphUserDict["first_name"] as NSString
println(first_name)
}
}
}
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
NSLog("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
NSLog("User signed up and logged in through Facebook!")
} else {
NSLog("User logged in through Facebook!")
problemFunc() // error is here
}
})
Ответ 2
Вот основные принципы игры: (от Apple docs: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID103)
"Глобальные и вложенные функции, введенные в функции, на самом деле являются особыми случаями замыканий. Замыкания берут одну из трех форм:
- Глобальные функции - это замыкания, которые имеют имя и не фиксируют никаких значений.
- Вложенные функции - это блокировки с именем и могут захватывать значения из их закрывающей функции.
- Выражения закрытия - это неназванные закрытия, написанные в легком синтаксисе, которые могут захватывать значения из их окружающего контекста.
т.е. это нормально
func someFunc() {
func nestFunc() {}
}
но это не
func someFunc() {
func nestFunc() {
func nestedFunc2() { }
}
}
Если вы посмотрите на это в Xcode, третья функция (func nestedFunc2) даст вам ошибку "Невозможно ссылаться на локальную функцию с захватом из другой локальной функции"
Верхняя функция (func someFunc) является глобальной функцией видимости, и они работают как обычные функции/методы.
Вторая функция (func nestFunc) - это вложенная функция, которая является именованным закрытием на один уровень, который может захватить область его родительской глобальной функции.
Вложенные функции могут захватывать область глобальной функции, но не область действия другой вложенной функции.
Вот почему нам нужно замыкание, т.е.
func someFunc() {
func nestFunc() {
let strictClosure = { () -> () in
//this is where you write the code
}
}
}
Ответ 3
@fluidsonic ответ должен решить проблему. Однако обратите внимание, что вы делаете код спагетти, потому что вы изменяете переменную, захваченную закрытием, и выполняете ее в контексте другой функции. Это трудно отследить, если вам нужно отлаживать и, как правило, сложно отслеживать, когда и как изменяется эта переменная.
Более линейный и лучше читаемый поток состоит в том, чтобы определить problemFunc
как функцию, берущую функцию как параметр, и вызывая эту функцию, а не напрямую устанавливая значение в переменной first_name
:
let problemFunc = { (callback: (String -> Void) -> ()) in
FBRequestConnection.startForMeWithCompletionHandler { (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if let fbGraphUserDict = result as? Dictionary<String, AnyObject>{
let first_name = fbGraphUserDict["first_name"] as NSString
callback(first_name) // << here you call the callback passing the `first_name` local variable
println(first_name)
}
}
}
и выполняйте фактическое присвоение first_name
в замыкании, которое вы определяете при вызове problemFunc
:
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
NSLog("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
NSLog("User signed up and logged in through Facebook!")
} else {
NSLog("User logged in through Facebook!")
problemFunc { (name: String) -> Void in
first_name = name
}
}
})