closure と mutating と Expected declaration

コードとして良いかどうかは非常に怪しいけど、とりあえずまとめて使ってみる練習。より詳しい解説とかは「Swift struct」とか「Swift mutating」でググればもっと良い記事があるかと思います。Google 検索の場合はツールから期間指定で1年以内とかやると新鮮な情報が出るかもしれない(最新の環境に適合しやすいコードサンプルとか)し、実用的なサンプルが出ないかもしれない。よくわからなければ「site:developer.apple.com」も検索引数につけて公式から探すとか(あるいはオフラインのヘルプ)。

ひとまず Stack という struct を型の指定問わず(Elementが仮の名前)で定義して、インスタンス作成時に指定できるようにする(サンプルコードでは String を Element の部分として指定)。で、push で値追加、pop で値を取得&削除、undo で削除しつつ、個別の取消し処理を実装できるように定義した感じ。なんとなく。

import UIKit

struct Stack<Element> {
    private var elements = [Element]()
    mutating func push(_ element: Element?) {
        if element == nil { return }
        elements.append(element!)
    }
    mutating func pop()->Element? {
        if elements.count <= 0 { return nil }
        return elements.removeLast()
    }
    mutating func undo(closure: (Element) -> Void) {
        guard let element = self.pop() else { return }   // とりあえず先に pop() する。element をなんとかすれば closure の行がこの行より先でも大丈夫
        closure(element)
    }
}

class ViewController: UIViewController {
    var history = Stack<String>()
    // self.history.push("foo bar buz")    // ここでメソッドを呼ぶと Expected declaration でコンパイルが通らない

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        self.history.push("1st text.")
        self.history.push("2nd text.")
        self.history.push("3rd text.")

        print("Before undo: \(self.history)")
        self.history.undo(closure: { (element) -> Void in
            print("Undo Element: \(element)")
        })
        print("After undo: \(self.history)")
    }
}

これを実行すると

Before undo: Stack<String>(elements: ["1st text.", "2nd text.", "3rd text."])
Undo Element: 3rd text.
After undo: Stack<String>(elements: ["1st text.", "2nd text."])

という出力になる。

mutating とは、公式のそのまま引用ですが

Modifying Value Types from Within Instance Methods

Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.

However, if you need to modify the properties of your structure or enumeration within a particular method, you can opt in to mutating behavior for that method. The method can then mutate (that is, change) its properties from within the method, and any changes that it makes are written back to the original structure when the method ends.

だそうで。

メソッド(上記では func push() とか)が呼ばれた時に、メソッドの処理完了の時点でメンバの値(上記では private var elements)に反映するイメージ。let じゃなくて var じゃないとアクセスできないのでエラーにしてやる!と公式に書いてあるきがする。

closure は

Closures are self-contained blocks of functionality that can be passed around and used in your code.

だそうです。他の解説サイトでは、名前をつけないでコード内に直接(メソッド呼ぶタイミング)記述できるよ的な。

上記では undo する時に、取り消す処理を(struct の定義内ではなく)個別に書きたいかなーと思ってあえて closure にした感じ。String に対してだけなら多分意味ないでしょうけど。

サンプルコードでは、self.pop() した(elements から最後の要素を削除)後にその値を受け取って、それに見合った取消し処理を記述するイメージ。イメージです。最後の要素が「3rd text.」で、closure 内ではそれに対して追加の処理を行える、かもしれない。

mutating 参照:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Methods.html

closure 参照:

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

フォローする