Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 676 - More Swift and Foundation Framework 2

講義3は文法的な説明が続きました。そして最後にPropertyListの説明とAniyObjectの関係の説明です。なんとなくぼんやりと考えていたPropertyListをCalculatorデモに組み込みます。

Developing iOS 9 Apps with Swift - Free Course by Stanford on iTunes U

計算結果を「save」して、後でその結果を「restore」する機能を組み込むのだと最初は思いました。そうじゃ無かったんです。program自体、計算式を「save」、例えば「3 × 7 + 6」という計算自体を「save」するという実装です。したがって、「restore」すると言うことは、「save」されている計算式を再計算させるという考え... うーん、こういう考え方は思いつきもしませんでした。この計算式をAnyObjectの集合体(DoubleとStringの混在だからです)として保存するんです。

 var program: AnyObject

これはprivateではなくpublic(internalのことです)にしてアクセスできるようにしてます。ここでドキュメンテーションという考え方から、「typealias」を使って、PropertyListでありながらAnyObjectであることを明白にします。

 typealias PropertyList = AnyObject

 var program: PropertyList

programは計算型プロパティとして使用します。

 var program: PropertyList {
        get{
            // Coding...
        }
        
        set {
           // Coding...
        }
    }

どうして計算型にするかと言えば、もう1つのprivateで外から見えないprogramを用意するからです。

それはinternalProgramとして用意します。これは「AnyObject」の集合体として用意するんです。プログラムのステップを保存する(こういうちょっとした保存はPropaertyList
として用意します。

    private var internalProgram = [AnyObject]()

internalProgramで計算のステップを次々に記録させていきます。自動で記録をさせるために「setOperand()」と「performOperation()」メソッドの両方に加えます。

// operandを押すとスタックに組み込まれます。
    func setOperand(operand: Double) {
        accumulator = operand
        internalProgram.append(operand) // 追加
    }
...
    func performOpeation(symbol: String) {
        internalProgram.append(symbol) // 追加
        if let operation = operations[symbol] {
            switch operation {
            case .Constant(let value):
                accumulator = value
            case .UnaryOperation(let function):
                accumulator = function(accumulator)
            case .BinaryOperation(let function):
                executePendingBinaryOperation()
                pending = 
                 PendingBinaryOperationInfo(binaryFunction: function, 
                   firstOperand: accumulator)
            case .Equals:
                executePendingBinaryOperation()
            }
        }
    }

これで自動的に計算ステップ(プログラム)が記録されていきます。そしてこれを外部から参照するために変数programを使って、コピーを扱うので計算型プロパティの「get」はシンプルに次のようになります。

 var program: PropertyList {
     get{
            return internalProgram
        }
        
        set {
           // Coding...
        }
    }

次は「set」です。internalProgramの値は次次と新しい値が加わっているので、保存していた[AnyObject]に再び計算をさせたい場合は、internalProgramを空にして、新しい[AnyObject」のデータで再計算させて、internalProgramを置き換えてやる必要があります。次のようにします。

        set {
            self.clear()
            if let arrayOfOps = newValue as? [AnyObject] {
                for op in arrayOfOps {
                    if let operand = op as? Double {
                        setOperand(operand)
                    } else if let operation = op as? String {
                        performOpeation(operation)
                    }
                }
            }

まず「clear()」メソッドはカスタムメソッドです。これは後でコードしますが、internalProgramを空にして計算をリセットするステップです。そして「newValue」はを[AnyObject]にキャストして、それぞれの項目でDoubleかStringで判断してメソッドを実行すれば、再計算がされながらinternalProgramにもセットされます。

clear()メソッドは簡単に次のように用意します。

    func clear() {
        accumulator = 0.0
        pending = nil
        internalProgram.removeAll()
    }

これでCalculatorBrainの実装は終了です。

次はMain.storyboardに移って、左済みの2つのボタン「e」と「cos」そそれぞれ「save」と「restore」にします。performOperation()メソッドに繋がっているので「右 + クリック」でコネクションを切ります。その後にViewControllerのコード内にそれぞれ「save()」と「restore()」メソッドを「Ctrl + ドラッグ」で作ります。

まず、CalculatorBrainで自動的に記録しているinternalProgramの内容を記録する変数を用意します。

    var savedProgram: CalculatorBrain.PropertyList?

「save」ボタンが存在するまで必要ないのでオプショナルにします。そして「save」ボタンを押した時に、この変数にCalculatorBrainのpragramを読み込めばいいわけです。

    @IBAction func save() {
        savedProgram = brain.program
    }

そしてrestoreするときは逆です。保存したプログラムをCalculatorBrainのprogramにセットすればいいんです。計算が実行されるので、表示用にresultも読み込み直す必要があります。

    @IBAction func restore() {
        
        if savedProgram != nil {
            brain.program = savedProgram!
            displayValue = brain.result
        }
    }

これで完璧です。ランします。ポートレイトでは「restore」ボタンが崩れるのでランドスケープにします。
f:id:yataiblue:20160514135817j:plain
これでCalculatorデモは終了です。今回はポール先生の説明していることが全て理解できました。まだ宿題(アサインメント)はしていないのですが、時間ができたらトライします。まずは授業についていかないといけないので明日から講義4に入ります。

今日はここまで。