これは何か
swiftのクロージャを連続して使うとネストが深くなってしまうのでそれを回避した
どんな処理を書きたかったか
こんな感じで処理を書きたかった
func hogeProcess(completionHandler: @escaping (Bool) -> Void) {
print("hoge")
completionHandler(true)
}
func someProcess(completionHandler: @escaping (Bool) -> Void) {
hogeProcess({ (success) in
if success == false { completionHandler(false) }
hogeProcess({ (success) in
if success == false { completionHandler(false) }
hogeProcess({ (success) in
if success == false { completionHandler(false) }
...
completionHandler(success)
...
})
})
})
}
someProcess() { (success) in
// Check status
print(success) // true
}
しかしネストが深すぎるのと、やろうとしていたことが hogeProcess
を行う回数がが動的に変化するためどうしても汚くなってしまった
解決方法(追記)
ループ毎に別の変数を用意してあげれば循環することなく実行される
func hogeProcess(completionHandler: @escaping (Bool) -> Void) {
print("hoge")
completionHandler(true)
}
func someProcess(completionHandler: @escaping (Bool)->Void) {
var current: (Bool)->Void = {
completionHandler($0)
}
for _ in 0..<3 {
let inner = current
current = { success in
if success == false { completionHandler(false) }
hogeProcess { success in
inner(success)
}
}
}
current(true)
}
someProcess() { (success) in
// Check status
print(success) // true
}
これを実行すると
$ swift main.swift
hoge
hoge
hoge
true
となる
解決方法(自分で考えたやつ)
関数を変数に代入できることをすっかり忘れていたので、それを使う
func hogeProcess(completionHandler: @escaping (Bool) -> Void) {
print("hoge")
completionHandler(true)
}
func someProcess(completionHandler: @escaping (Bool) -> Void) {
func hogeCompletionHandler(success: Bool) {
completionHandler(success)
}
var functions = [hogeCompletionHandler]
for var i in 0..<3 { // 例えば3回やる時
func tmpProcess(success: Bool) {
if success == false { completionHandler(false) }
hogeProcess() { (success) in
_ = functions.removeLast()
functions.last!(success)
}
}
functions.append(tmpProcess)
}
functions.last!(true)
}
someProcess() { (success) in
// Check status
print(success)
}
これを実行してみると、
$ swift main.swift
hoge
hoge
hoge
true
となる
何をしているか
関数を配列に入れてそれをよんでいる
展開するとこんな感じ
// 配列内の一番最後の関数
if success == false { completionHandler(false) }
hogeProcess() { (success) in
_ = functions.removeLast() // 最後の関数を消す (消さないと循環してしまう)
functions.last!(success) // 更新された最後の関数を実行
}
これを再帰的に行うので一番最後のクロージャで最初に入れた関数が実行される仕組み
最初に試してダメだったやつ
最初に行けるのでは?と思ったけどダメだったやつ
func lastFunction() {
print("last")
}
var function = lastFunction
for var i in 0..<3 {
func process() {
print("process")
function()
}
function = process
}
function()
こうすると永遠に process
という文字列を出力し続けてしまう
function = process
の時点で function
の中で function
を読んでしまう処理になってしまい、
{ // function の中身
print("process")
function()
}
こんな感じになってしまうのでダメだった
呼び出す関数名がユニークならばよかったが同一なので別物としてどこかに残しておく必要があったため配列に入れた
まとめ
こんな処理書きたくなかった…
これ以外にいい方法があれば教えてください