Help us understand the problem. What is going on with this article?

Swiftのクロージャを乱用したかった

More than 1 year has passed since last update.

 これは何か

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()
}

こんな感じになってしまうのでダメだった

呼び出す関数名がユニークならばよかったが同一なので別物としてどこかに残しておく必要があったため配列に入れた

まとめ

こんな処理書きたくなかった…
これ以外にいい方法があれば教えてください

tantakan
天才美少女
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした